diff --git a/CpuToYourEars.sln b/CpuToYourEars.sln
new file mode 100644
index 0000000..f1f9686
--- /dev/null
+++ b/CpuToYourEars.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26730.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CpuToYourEars", "CpuToYourEars\CpuToYourEars.csproj", "{BE9E02BB-0939-4AE7-84F4-EDAA78FE6505}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BE9E02BB-0939-4AE7-84F4-EDAA78FE6505}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BE9E02BB-0939-4AE7-84F4-EDAA78FE6505}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BE9E02BB-0939-4AE7-84F4-EDAA78FE6505}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BE9E02BB-0939-4AE7-84F4-EDAA78FE6505}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {9B0A5D14-0F2C-4F83-B2E1-1B2C0A325FDF}
+ EndGlobalSection
+EndGlobal
diff --git a/CpuToYourEars/App.config b/CpuToYourEars/App.config
new file mode 100644
index 0000000..662f695
--- /dev/null
+++ b/CpuToYourEars/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CpuToYourEars/CommandLineArguments.cs b/CpuToYourEars/CommandLineArguments.cs
new file mode 100644
index 0000000..408b4c5
--- /dev/null
+++ b/CpuToYourEars/CommandLineArguments.cs
@@ -0,0 +1,1559 @@
+//////////////////////////////////////////////////////////////////////////////
+// Command Line Argument Parser
+// ----------------------------
+//
+// Author: peterhal@microsoft.com
+//
+// Shared Source License for Command Line Parser Library
+//
+// This license governs use of the accompanying software ('Software'), and your
+// use of the Software constitutes acceptance of this license.
+//
+// You may use the Software for any commercial or noncommercial purpose,
+// including distributing derivative works.
+//
+// In return, we simply require that you agree:
+//
+// 1. Not to remove any copyright or other notices from the Software.
+// 2. That if you distribute the Software in source code form you do so only
+// under this license (i.e. you must include a complete copy of this license
+// with your distribution), and if you distribute the Software solely in
+// object form you only do so under a license that complies with this
+// license.
+// 3. That the Software comes "as is", with no warranties. None whatsoever.
+// This means no express, implied or statutory warranty, including without
+// limitation, warranties of merchantability or fitness for a particular
+// purpose or any warranty of title or non-infringement. Also, you must pass
+// this disclaimer on whenever you distribute the Software or derivative
+// works.
+// 4. That no contributor to the Software will be liable for any of those types
+// of damages known as indirect, special, consequential, or incidental
+// related to the Software or this license, to the maximum extent the law
+// permits, no matter what legal theory it’s based on. Also, you must pass
+// this limitation of liability on whenever you distribute the Software or
+// derivative works.
+// 5. That if you sue anyone over patents that you think may apply to the
+// Software for a person's use of the Software, your license to the Software
+// ends automatically.
+// 6. That the patent rights, if any, granted in this license only apply to the
+// Software, not to any derivative works you make.
+// 7. That the Software is subject to U.S. export jurisdiction at the time it
+// is licensed to you, and it may be subject to additional export or import
+// laws in other places. You agree to comply with all such laws and
+// regulations that may apply to the Software after delivery of the software
+// to you.
+// 8. That if you are an agency of the U.S. Government, (i) Software provided
+// pursuant to a solicitation issued on or after December 1, 1995, is
+// provided with the commercial license rights set forth in this license,
+// and (ii) Software provided pursuant to a solicitation issued prior to
+// December 1, 1995, is provided with “Restricted Rights” as set forth in
+// FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013
+// (Oct 1988), as applicable.
+// 9. That your rights under this License end automatically if you breach it in
+// any way.
+// 10.That all rights not expressly granted to you in this license are reserved.
+//
+// Usage
+// -----
+//
+// Parsing command line arguments to a console application is a common problem.
+// This library handles the common task of reading arguments from a command line
+// and filling in the values in a type.
+//
+// To use this library, define a class whose fields represent the data that your
+// application wants to receive from arguments on the command line. Then call
+// CommandLine.ParseArguments() to fill the object with the data
+// from the command line. Each field in the class defines a command line argument.
+// The type of the field is used to validate the data read from the command line.
+// The name of the field defines the name of the command line option.
+//
+// The parser can handle fields of the following types:
+//
+// - string
+// - int
+// - uint
+// - bool
+// - enum
+// - array of the above type
+//
+// For example, suppose you want to read in the argument list for wc (word count).
+// wc takes three optional boolean arguments: -l, -w, and -c and a list of files.
+//
+// You could parse these arguments using the following code:
+//
+// class WCArguments
+// {
+// public bool lines;
+// public bool words;
+// public bool chars;
+// public string[] files;
+// }
+//
+// class WC
+// {
+// static void Main(string[] args)
+// {
+// if (CommandLine.ParseArgumentsWithUsage(args, parsedArgs))
+// {
+// // insert application code here
+// }
+// }
+// }
+//
+// So you could call this aplication with the following command line to count
+// lines in the foo and bar files:
+//
+// wc.exe /lines /files:foo /files:bar
+//
+// The program will display the following usage message when bad command line
+// arguments are used:
+//
+// wc.exe -x
+//
+// Unrecognized command line argument '-x'
+// /lines[+|-] short form /l
+// /words[+|-] short form /w
+// /chars[+|-] short form /c
+// /files: short form /f
+// @ Read response file for more options
+//
+// That was pretty easy. However, you realy want to omit the "/files:" for the
+// list of files. The details of field parsing can be controled using custom
+// attributes. The attributes which control parsing behaviour are:
+//
+// ArgumentAttribute
+// - controls short name, long name, required, allow duplicates, default value
+// and help text
+// DefaultArgumentAttribute
+// - allows omition of the "/name".
+// - This attribute is allowed on only one field in the argument class.
+//
+// So for the wc.exe program we want this:
+//
+// using System;
+// using Utilities;
+//
+// class WCArguments
+// {
+// [Argument(ArgumentType.AtMostOnce, HelpText="Count number of lines in the input text.")]
+// public bool lines;
+// [Argument(ArgumentType.AtMostOnce, HelpText="Count number of words in the input text.")]
+// public bool words;
+// [Argument(ArgumentType.AtMostOnce, HelpText="Count number of chars in the input text.")]
+// public bool chars;
+// [DefaultArgument(ArgumentType.MultipleUnique, HelpText="Input files to count.")]
+// public string[] files;
+// }
+//
+// class WC
+// {
+// static void Main(string[] args)
+// {
+// WCArguments parsedArgs = new WCArguments();
+// if (CommandLine.ParseArgumentsWithUsage(args, parsedArgs))
+// {
+// // insert application code here
+// }
+// }
+// }
+//
+//
+//
+// So now we have the command line we want:
+//
+// wc.exe /lines foo bar
+//
+// This will set lines to true and will set files to an array containing the
+// strings "foo" and "bar".
+//
+// The new usage message becomes:
+//
+// wc.exe -x
+//
+// Unrecognized command line argument '-x'
+// /lines[+|-] Count number of lines in the input text. (short form /l)
+// /words[+|-] Count number of words in the input text. (short form /w)
+// /chars[+|-] Count number of chars in the input text. (short form /c)
+// @ Read response file for more options
+// Input files to count. (short form /f)
+//
+// If you want more control over how error messages are reported, how /help is
+// dealt with, etc you can instantiate the CommandLine.Parser class.
+//
+//
+//
+// Cheers,
+// Peter Hallam
+// C# Compiler Developer
+// Microsoft Corp.
+//
+//
+//
+//
+// Release Notes
+// -------------
+//
+// 10/02/2002 Initial Release
+// 10/14/2002 Bug Fix
+// 01/08/2003 Bug Fix in @ include files
+// 10/23/2004 Added user specified help text, formatting of help text to
+// screen width. Added ParseHelp for /?.
+// 11/23/2004 Added support for default values.
+// 02/23/2005 Fix bug with short name and default arguments.
+// 03/12/2007 Added support for non case sensitive option names.
+//////////////////////////////////////////////////////////////////////////////
+
+namespace Microsoft.CommandLineHelper
+{
+ using System;
+ using System.Diagnostics;
+ using System.Reflection;
+ using System.Collections;
+ using System.IO;
+ using System.Text;
+ using System.Runtime.InteropServices;
+
+ ///
+ /// Used to control parsing of command line arguments.
+ ///
+ [Flags]
+ public enum ArgumentType
+ {
+ ///
+ /// Indicates that this field is required. An error will be displayed
+ /// if it is not present when parsing arguments.
+ ///
+ Required = 0x01,
+ ///
+ /// Only valid in conjunction with Multiple.
+ /// Duplicate values will result in an error.
+ ///
+ Unique = 0x02,
+ ///
+ /// Inidicates that the argument may be specified more than once.
+ /// Only valid if the argument is a collection
+ ///
+ Multiple = 0x04,
+
+ ///
+ /// The default type for non-collection arguments.
+ /// The argument is not required, but an error will be reported if it is specified more than once.
+ ///
+ AtMostOnce = 0x00,
+
+ ///
+ /// For non-collection arguments, when the argument is specified more than
+ /// once no error is reported and the value of the argument is the last
+ /// value which occurs in the argument list.
+ ///
+ LastOccurenceWins = Multiple,
+
+ ///
+ /// The default type for collection arguments.
+ /// The argument is permitted to occur multiple times, but duplicate
+ /// values will cause an error to be reported.
+ ///
+ MultipleUnique = Multiple | Unique,
+ }
+
+ ///
+ /// Allows control of command line parsing.
+ /// Attach this attribute to instance fields of types used
+ /// as the destination of command line argument parsing.
+ ///
+ [AttributeUsage(AttributeTargets.Field)]
+ public class ArgumentAttribute : Attribute
+ {
+ ///
+ /// Allows control of command line parsing.
+ ///
+ /// Specifies the error checking to be done on the argument.
+ public ArgumentAttribute(ArgumentType type)
+ {
+ this.type = type;
+ }
+
+ ///
+ /// The error checking to be done on the argument.
+ ///
+ public ArgumentType Type
+ {
+ get { return this.type; }
+ }
+ ///
+ /// Returns true if the argument did not have an explicit short name specified.
+ ///
+ public bool DefaultShortName { get { return null == this.shortName; } }
+
+ ///
+ /// The short name of the argument.
+ /// Set to null means use the default short name if it does not
+ /// conflict with any other parameter name.
+ /// Set to String.Empty for no short name.
+ /// This property should not be set for DefaultArgumentAttributes.
+ ///
+ public string ShortName
+ {
+ get { return this.shortName; }
+ set { Debug.Assert(value == null || !(this is DefaultArgumentAttribute)); this.shortName = value; }
+ }
+
+ ///
+ /// Returns true if the argument did not have an explicit long name specified.
+ ///
+ public bool DefaultLongName { get { return null == this.longName; } }
+
+ ///
+ /// The long name of the argument.
+ /// Set to null means use the default long name.
+ /// The long name for every argument must be unique.
+ /// It is an error to specify a long name of String.Empty.
+ ///
+ public string LongName
+ {
+ get { Debug.Assert(!this.DefaultLongName); return this.longName; }
+ set { Debug.Assert(value != ""); this.longName = value; }
+ }
+
+ ///
+ /// The default value of the argument.
+ ///
+ public object DefaultValue
+ {
+ get { return this.defaultValue; }
+ set { this.defaultValue = value; }
+ }
+
+ ///
+ /// Returns true if the argument has a default value.
+ ///
+ public bool HasDefaultValue { get { return null != this.defaultValue; } }
+
+ ///
+ /// Returns true if the argument has help text specified.
+ ///
+ public bool HasHelpText { get { return null != this.helpText; } }
+
+ ///
+ /// The help text for the argument.
+ ///
+ public string HelpText
+ {
+ get { return this.helpText; }
+ set { this.helpText = value; }
+ }
+
+ private string shortName;
+ private string longName;
+ private string helpText;
+ private object defaultValue;
+ private ArgumentType type;
+ }
+
+ ///
+ /// Indicates that this argument is the default argument.
+ /// '/' or '-' prefix only the argument value is specified.
+ /// The ShortName property should not be set for DefaultArgumentAttribute
+ /// instances. The LongName property is used for usage text only and
+ /// does not affect the usage of the argument.
+ ///
+ [AttributeUsage(AttributeTargets.Field)]
+ public class DefaultArgumentAttribute : ArgumentAttribute
+ {
+ ///
+ /// Indicates that this argument is the default argument.
+ ///
+ /// Specifies the error checking to be done on the argument.
+ public DefaultArgumentAttribute(ArgumentType type)
+ : base (type)
+ {
+ }
+ }
+
+ ///
+ /// Indicates that a type represents command line arguments.
+ /// adding this attribute is optional, and can be sued to override
+ /// default behavior for case sensitivity.
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
+ public class CommandLineArgumentsAttribute : Attribute
+ {
+ public CommandLineArgumentsAttribute() { }
+
+ ///
+ /// Should argument name parsing respect case
+ ///
+ public bool CaseSensitive
+ {
+ get { return this.caseSensitive; }
+ set { this.caseSensitive = value; }
+ }
+
+ private bool caseSensitive = true;
+ }
+
+ ///
+ /// A delegate used in error reporting.
+ ///
+ public delegate void ErrorReporter(string message);
+
+ ///
+ /// Parser for command line arguments.
+ ///
+ /// The parser specification is infered from the instance fields of the object
+ /// specified as the destination of the parse.
+ /// Valid argument types are: int, uint, string, bool, enums
+ /// Also argument types of Array of the above types are also valid.
+ ///
+ /// Error checking options can be controlled by adding a ArgumentAttribute
+ /// to the instance fields of the destination object.
+ ///
+ /// At most one field may be marked with the DefaultArgumentAttribute
+ /// indicating that arguments without a '-' or '/' prefix will be parsed as that argument.
+ ///
+ /// If not specified then the parser will infer default options for parsing each
+ /// instance field. The default long name of the argument is the field name. The
+ /// default short name is the first character of the long name. Long names and explicitly
+ /// specified short names must be unique. Default short names will be used provided that
+ /// the default short name does not conflict with a long name or an explicitly
+ /// specified short name.
+ ///
+ /// Arguments which are array types are collection arguments. Collection
+ /// arguments can be specified multiple times.
+ ///
+ public sealed class Parser
+ {
+ ///
+ /// The System Defined new line string.
+ ///
+ public const string NewLine = "\r\n";
+
+ ///
+ /// Don't ever call this.
+ ///
+ private Parser() { }
+
+ ///
+ /// Parses Command Line Arguments. Displays usage message to Console.Out
+ /// if /?, /help or invalid arguments are encounterd.
+ /// Errors are output on Console.Error.
+ /// Use ArgumentAttributes to control parsing behaviour.
+ ///
+ /// The actual arguments.
+ /// The resulting parsed arguments.
+ /// true if no errors were detected.
+ public static bool ParseArgumentsWithUsage(string [] arguments, object destination)
+ {
+ if (Parser.ParseHelp(arguments) || !Parser.ParseArguments(arguments, destination))
+ {
+ // error encountered in arguments. Display usage message
+ System.Console.Write(Parser.ArgumentsUsage(destination.GetType()));
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Parses Command Line Arguments.
+ /// Errors are output on Console.Error.
+ /// Use ArgumentAttributes to control parsing behaviour.
+ ///
+ /// The actual arguments.
+ /// The resulting parsed arguments.
+ /// true if no errors were detected.
+ public static bool ParseArguments(string [] arguments, object destination)
+ {
+ return Parser.ParseArguments(arguments, destination, new ErrorReporter(Console.Error.WriteLine));
+ }
+
+ ///
+ /// Parses Command Line Arguments.
+ /// Use ArgumentAttributes to control parsing behaviour.
+ ///
+ /// The actual arguments.
+ /// The resulting parsed arguments.
+ /// The destination for parse errors.
+ /// true if no errors were detected.
+ public static bool ParseArguments(string[] arguments, object destination, ErrorReporter reporter)
+ {
+ Parser parser = new Parser(destination.GetType(), reporter);
+ return parser.Parse(arguments, destination);
+ }
+
+ private static void NullErrorReporter(string message)
+ {
+ }
+
+ private class HelpArgument
+ {
+ [ArgumentAttribute(ArgumentType.AtMostOnce, ShortName="?")]
+ public bool help = false;
+ }
+
+ ///
+ /// Checks if a set of arguments asks for help.
+ ///
+ /// Args to check for help.
+ /// Returns true if args contains /? or /help.
+ public static bool ParseHelp(string[] args)
+ {
+ Parser helpParser = new Parser(typeof(HelpArgument), new ErrorReporter(NullErrorReporter));
+ HelpArgument helpArgument = new HelpArgument();
+ helpParser.Parse(args, helpArgument);
+ return helpArgument.help;
+ }
+
+
+ ///
+ /// Returns a Usage string for command line argument parsing.
+ /// Use ArgumentAttributes to control parsing behaviour.
+ /// Formats the output to the width of the current console window.
+ ///
+ /// The type of the arguments to display usage for.
+ /// Printable string containing a user friendly description of command line arguments.
+ public static string ArgumentsUsage(Type argumentType)
+ {
+ int screenWidth = Parser.GetConsoleWindowWidth();
+ if (screenWidth == 0)
+ screenWidth = 80;
+ return ArgumentsUsage(argumentType, screenWidth);
+ }
+
+ ///
+ /// Returns a Usage string for command line argument parsing.
+ /// Use ArgumentAttributes to control parsing behaviour.
+ ///
+ /// The type of the arguments to display usage for.
+ /// The number of columns to format the output to.
+ /// Printable string containing a user friendly description of command line arguments.
+ public static string ArgumentsUsage(Type argumentType, int columns)
+ {
+ return (new Parser(argumentType, null)).GetUsageString(columns);
+ }
+
+ private const int STD_OUTPUT_HANDLE = -11;
+
+ private struct COORD
+ {
+ internal Int16 x;
+ internal Int16 y;
+ }
+
+ private struct SMALL_RECT
+ {
+ internal Int16 Left;
+ internal Int16 Top;
+ internal Int16 Right;
+ internal Int16 Bottom;
+ }
+
+ private struct CONSOLE_SCREEN_BUFFER_INFO
+ {
+ internal COORD dwSize;
+ internal COORD dwCursorPosition;
+ internal Int16 wAttributes;
+ internal SMALL_RECT srWindow;
+ internal COORD dwMaximumWindowSize;
+ }
+
+ [DllImport("kernel32.dll", EntryPoint="GetStdHandle", SetLastError=true, CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
+ private static extern int GetStdHandle(int nStdHandle);
+
+ [DllImport("kernel32.dll", EntryPoint="GetConsoleScreenBufferInfo", SetLastError=true, CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
+ private static extern int GetConsoleScreenBufferInfo(int hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);
+
+ ///
+ /// Returns the number of columns in the current console window
+ ///
+ /// Returns the number of columns in the current console window
+ public static int GetConsoleWindowWidth()
+ {
+ int screenWidth;
+ CONSOLE_SCREEN_BUFFER_INFO csbi = new CONSOLE_SCREEN_BUFFER_INFO();
+
+ int rc;
+ rc = GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), ref csbi);
+ screenWidth = csbi.dwSize.x;
+ return screenWidth;
+ }
+
+ ///
+ /// Searches a StringBuilder for a character
+ ///
+ /// The text to search.
+ /// The character value to search for.
+ /// The index to stat searching at.
+ /// The index of the first occurence of value or -1 if it is not found.
+ public static int IndexOf(StringBuilder text, char value, int startIndex)
+ {
+ for (int index = startIndex; index < text.Length; index++)
+ {
+ if (text[index] == value)
+ return index;
+ }
+
+ return -1;
+ }
+
+ ///
+ /// Searches a StringBuilder for a character in reverse
+ ///
+ /// The text to search.
+ /// The character to search for.
+ /// The index to start the search at.
+ /// The index of the last occurence of value in text or -1 if it is not found.
+ public static int LastIndexOf(StringBuilder text, char value, int startIndex)
+ {
+ for (int index = Math.Min(startIndex, text.Length - 1); index >= 0; index --)
+ {
+ if (text[index] == value)
+ return index;
+ }
+
+ return -1;
+ }
+
+ private const int spaceBeforeParam = 2;
+
+ ///
+ /// Creates a new command line argument parser.
+ ///
+ /// The type of object to parse.
+ /// The destination for parse errors.
+ public Parser(Type argumentSpecification, ErrorReporter reporter)
+ {
+ this.reporter = reporter;
+ this.arguments = new ArrayList();
+ CommandLineArgumentsAttribute typeAttribute = GetAttribute(argumentSpecification);
+ bool caseSensitive = (typeAttribute == null || typeAttribute.CaseSensitive);
+ this.argumentMap = new Hashtable(caseSensitive ? StringComparer.Ordinal : StringComparer.InvariantCultureIgnoreCase);
+
+ foreach (FieldInfo field in argumentSpecification.GetFields())
+ {
+ if (!field.IsStatic && !field.IsInitOnly && !field.IsLiteral)
+ {
+ ArgumentAttribute attribute = GetAttribute(field);
+ if (attribute is DefaultArgumentAttribute)
+ {
+ Debug.Assert(this.defaultArgument == null);
+ this.defaultArgument = new Argument(attribute, field, reporter);
+ }
+ else
+ {
+ this.arguments.Add(new Argument(attribute, field, reporter));
+ }
+ }
+ }
+
+ AddExplicitArgumentNames();
+ AddImplicitArgumentNames();
+ }
+
+ private static ArgumentAttribute GetAttribute(FieldInfo field)
+ {
+ return (ArgumentAttribute)GetAttribute(field, typeof(ArgumentAttribute));
+ }
+
+ private static CommandLineArgumentsAttribute GetAttribute(Type type)
+ {
+ return (CommandLineArgumentsAttribute)GetAttribute(type, typeof(CommandLineArgumentsAttribute));
+ }
+
+ private static object GetAttribute(MemberInfo member, Type attributeType)
+ {
+ object[] attributes = member.GetCustomAttributes(attributeType, false);
+ if (attributes.Length == 1)
+ return attributes[0];
+
+ Debug.Assert(attributes.Length == 0);
+ return null;
+ }
+
+ private void AddExplicitArgumentNames()
+ {
+ foreach (Argument argument in this.arguments)
+ {
+ Debug.Assert(!this.argumentMap.ContainsKey(argument.LongName));
+
+ this.argumentMap[argument.LongName] = argument;
+ if (argument.ExplicitShortName)
+ {
+ if (argument.HasShortName)
+ {
+ Debug.Assert(!this.argumentMap.ContainsKey(argument.ShortName));
+
+ this.argumentMap[argument.ShortName] = argument;
+ }
+ else
+ {
+ argument.ClearShortName();
+ }
+ }
+ }
+ }
+
+ private void AddImplicitArgumentNames()
+ {
+ foreach (Argument argument in this.arguments)
+ {
+ if (!argument.ExplicitShortName)
+ {
+ if (argument.HasShortName && !this.argumentMap.ContainsKey(argument.ShortName))
+ {
+ this.argumentMap[argument.ShortName] = argument;
+ }
+ else
+ {
+ argument.ClearShortName();
+ }
+ }
+ }
+ }
+
+ private void ReportUnrecognizedArgument(string argument)
+ {
+ this.reporter(string.Format("Unrecognized command line argument '{0}'", argument));
+ }
+
+ ///
+ /// Parses an argument list into an object
+ ///
+ ///
+ ///
+ /// true if an error occurred
+ private bool ParseArgumentList(string[] args, object destination)
+ {
+ bool hadError = false;
+ if (args != null)
+ {
+ foreach (string argument in args)
+ {
+ if (argument.Length > 0)
+ {
+ switch (argument[0])
+ {
+ case '-':
+ case '/':
+ string option;
+ string optionArgument;
+ GetOptionNameAndArgument(argument, out option, out optionArgument);
+
+ // check for both case sensitive and case insensitive options
+ Argument arg = ArgumentFromString(option);
+
+ if (arg == null)
+ {
+ ReportUnrecognizedArgument(argument);
+ hadError = true;
+ }
+ else
+ {
+ hadError |= !arg.SetValue(optionArgument, destination);
+ }
+ break;
+ case '@':
+ string[] nestedArguments;
+ hadError |= LexFileArguments(argument.Substring(1), out nestedArguments);
+ hadError |= ParseArgumentList(nestedArguments, destination);
+ break;
+ default:
+ if (this.defaultArgument != null)
+ {
+ hadError |= !this.defaultArgument.SetValue(argument, destination);
+ }
+ else
+ {
+ ReportUnrecognizedArgument(argument);
+ hadError = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return hadError;
+ }
+
+ private static void GetOptionNameAndArgument(string argument, out string option, out string optionArgument)
+ {
+ int endIndex = argument.IndexOfAny(new char[] { ':', '+', '-' }, 1);
+ option = argument.Substring(1, endIndex == -1 ? argument.Length - 1 : endIndex - 1);
+
+ if (option.Length + 1 == argument.Length)
+ {
+ optionArgument = null;
+ }
+ else if (argument.Length > 1 + option.Length && argument[1 + option.Length] == ':')
+ {
+ optionArgument = argument.Substring(option.Length + 2);
+ }
+ else
+ {
+ optionArgument = argument.Substring(option.Length + 1);
+ }
+ }
+
+ private Argument ArgumentFromString(string option)
+ {
+ return (Argument)this.argumentMap[option];
+ }
+
+ ///
+ /// Parses an argument list.
+ ///
+ /// The arguments to parse.
+ /// The destination of the parsed arguments.
+ /// true if no parse errors were encountered.
+ public bool Parse(string[] args, object destination)
+ {
+ bool hadError = ParseArgumentList(args, destination);
+
+ // check for missing required arguments
+ foreach (Argument arg in this.arguments)
+ {
+ hadError |= arg.Finish(destination);
+ }
+ if (this.defaultArgument != null)
+ {
+ hadError |= this.defaultArgument.Finish(destination);
+ }
+
+ return !hadError;
+ }
+
+ private struct ArgumentHelpStrings
+ {
+ public ArgumentHelpStrings(string syntax, string help)
+ {
+ this.syntax = syntax;
+ this.help = help;
+ }
+
+ public string syntax;
+ public string help;
+ }
+
+ ///
+ /// A user firendly usage string describing the command line argument syntax.
+ ///
+ public string GetUsageString(int screenWidth)
+ {
+ ArgumentHelpStrings[] strings = GetAllHelpStrings();
+
+ int maxParamLen = 0;
+ foreach (ArgumentHelpStrings helpString in strings)
+ {
+ maxParamLen = Math.Max(maxParamLen, helpString.syntax.Length);
+ }
+
+ const int minimumNumberOfCharsForHelpText = 10;
+ const int minimumHelpTextColumn = 5;
+ const int minimumScreenWidth = minimumHelpTextColumn + minimumNumberOfCharsForHelpText;
+
+ int helpTextColumn;
+ int idealMinimumHelpTextColumn = maxParamLen + spaceBeforeParam;
+ screenWidth = Math.Max(screenWidth, minimumScreenWidth);
+ if (screenWidth < (idealMinimumHelpTextColumn + minimumNumberOfCharsForHelpText))
+ helpTextColumn = minimumHelpTextColumn;
+ else
+ helpTextColumn = idealMinimumHelpTextColumn;
+
+ const string newLine = "\n";
+ StringBuilder builder = new StringBuilder();
+ foreach (ArgumentHelpStrings helpStrings in strings)
+ {
+ // add syntax string
+ int syntaxLength = helpStrings.syntax.Length;
+ builder.Append(helpStrings.syntax);
+
+ // start help text on new line if syntax string is too long
+ int currentColumn = syntaxLength;
+ if (syntaxLength >= helpTextColumn)
+ {
+ builder.Append(newLine);
+ currentColumn = 0;
+ }
+
+ // add help text broken on spaces
+ int charsPerLine = screenWidth - helpTextColumn;
+ int index = 0;
+ while (index < helpStrings.help.Length)
+ {
+ // tab to start column
+ builder.Append(' ', helpTextColumn - currentColumn);
+ currentColumn = helpTextColumn;
+
+ // find number of chars to display on this line
+ int endIndex = index + charsPerLine;
+ if (endIndex >= helpStrings.help.Length)
+ {
+ // rest of text fits on this line
+ endIndex = helpStrings.help.Length;
+ }
+ else
+ {
+ endIndex = helpStrings.help.LastIndexOf(' ', endIndex - 1, Math.Min(endIndex - index, charsPerLine));
+ if (endIndex <= index)
+ {
+ // no spaces on this line, append full set of chars
+ endIndex = index + charsPerLine;
+ }
+ }
+
+ // add chars
+ builder.Append(helpStrings.help, index, endIndex - index);
+ index = endIndex;
+
+ // do new line
+ AddNewLine(newLine, builder, ref currentColumn);
+
+ // don't start a new line with spaces
+ while (index < helpStrings.help.Length && helpStrings.help[index] == ' ')
+ index ++;
+ }
+
+ // add newline if there's no help text
+ if (helpStrings.help.Length == 0)
+ {
+ builder.Append(newLine);
+ }
+ }
+
+ return builder.ToString();
+ }
+ private static void AddNewLine(string newLine, StringBuilder builder, ref int currentColumn)
+ {
+ builder.Append(newLine);
+ currentColumn = 0;
+ }
+ private ArgumentHelpStrings[] GetAllHelpStrings()
+ {
+ ArgumentHelpStrings[] strings = new ArgumentHelpStrings[NumberOfParametersToDisplay()];
+
+ int index = 0;
+ foreach (Argument arg in this.arguments)
+ {
+ strings[index] = GetHelpStrings(arg);
+ index++;
+ }
+
+ // Commenting out line below because we don't want to show @ argument
+ //strings[index++] = new ArgumentHelpStrings("@", "Read response file for more options");
+
+ if (this.defaultArgument != null)
+ strings[index++] = GetHelpStrings(this.defaultArgument);
+
+ return strings;
+ }
+
+ private static ArgumentHelpStrings GetHelpStrings(Argument arg)
+ {
+ return new ArgumentHelpStrings(arg.SyntaxHelp, arg.FullHelpText);
+ }
+
+ private int NumberOfParametersToDisplay()
+ {
+ int numberOfParameters = this.arguments.Count;
+
+ // Commenting out line below because we don't want to show @ argument
+ //numberOfParameters++;
+
+ if (HasDefaultArgument)
+ numberOfParameters += 1;
+ return numberOfParameters;
+ }
+
+ ///
+ /// Does this parser have a default argument.
+ ///
+ /// Does this parser have a default argument.
+ public bool HasDefaultArgument
+ {
+ get { return this.defaultArgument != null; }
+ }
+
+ private bool LexFileArguments(string fileName, out string[] arguments)
+ {
+ string args = null;
+
+ try
+ {
+ using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read))
+ {
+ args = (new StreamReader(file)).ReadToEnd();
+ }
+ }
+ catch (Exception e)
+ {
+ this.reporter(string.Format("Error: Can't open command line argument file '{0}' : '{1}'", fileName, e.Message));
+ arguments = null;
+ return false;
+ }
+
+ bool hadError = false;
+ ArrayList argArray = new ArrayList();
+ StringBuilder currentArg = new StringBuilder();
+ bool inQuotes = false;
+ int index = 0;
+
+ // while (index < args.Length)
+ try
+ {
+ while (true)
+ {
+ // skip whitespace
+ while (char.IsWhiteSpace(args[index]))
+ {
+ index += 1;
+ }
+
+ // # - comment to end of line
+ if (args[index] == '#')
+ {
+ index += 1;
+ while (args[index] != '\n')
+ {
+ index += 1;
+ }
+ continue;
+ }
+
+ // do one argument
+ do
+ {
+ if (args[index] == '\\')
+ {
+ int cSlashes = 1;
+ index += 1;
+ while (index == args.Length && args[index] == '\\')
+ {
+ cSlashes += 1;
+ }
+
+ if (index == args.Length || args[index] != '"')
+ {
+ currentArg.Append('\\', cSlashes);
+ }
+ else
+ {
+ currentArg.Append('\\', (cSlashes >> 1));
+ if (0 != (cSlashes & 1))
+ {
+ currentArg.Append('"');
+ }
+ else
+ {
+ inQuotes = !inQuotes;
+ }
+ }
+ }
+ else if (args[index] == '"')
+ {
+ inQuotes = !inQuotes;
+ index += 1;
+ }
+ else
+ {
+ currentArg.Append(args[index]);
+ index += 1;
+ }
+ } while (!char.IsWhiteSpace(args[index]) || inQuotes);
+ argArray.Add(currentArg.ToString());
+ currentArg.Length = 0;
+ }
+ }
+ catch (System.IndexOutOfRangeException)
+ {
+ // got EOF
+ if (inQuotes)
+ {
+ this.reporter(string.Format("Error: Unbalanced '\"' in command line argument file '{0}'", fileName));
+ hadError = true;
+ }
+ else if (currentArg.Length > 0)
+ {
+ // valid argument can be terminated by EOF
+ argArray.Add(currentArg.ToString());
+ }
+ }
+
+ arguments = (string[]) argArray.ToArray(typeof (string));
+ return hadError;
+ }
+
+ private static string LongName(ArgumentAttribute attribute, FieldInfo field)
+ {
+ return (attribute == null || attribute.DefaultLongName) ? field.Name : attribute.LongName;
+ }
+
+ private static string ShortName(ArgumentAttribute attribute, FieldInfo field)
+ {
+ if (attribute is DefaultArgumentAttribute)
+ return null;
+ if (!ExplicitShortName(attribute))
+ return LongName(attribute, field).Substring(0,1);
+ return attribute.ShortName;
+ }
+
+ private static string HelpText(ArgumentAttribute attribute, FieldInfo field)
+ {
+ if (attribute == null)
+ return null;
+ else
+ return attribute.HelpText;
+ }
+
+ private static bool HasHelpText(ArgumentAttribute attribute)
+ {
+ return (attribute != null && attribute.HasHelpText);
+ }
+
+ private static bool ExplicitShortName(ArgumentAttribute attribute)
+ {
+ return (attribute != null && !attribute.DefaultShortName);
+ }
+
+ private static object DefaultValue(ArgumentAttribute attribute, FieldInfo field)
+ {
+ return (attribute == null || !attribute.HasDefaultValue) ? null : attribute.DefaultValue;
+ }
+
+ private static Type ElementType(FieldInfo field)
+ {
+ if (IsCollectionType(field.FieldType))
+ return field.FieldType.GetElementType();
+ else
+ return null;
+ }
+
+ private static ArgumentType Flags(ArgumentAttribute attribute, FieldInfo field)
+ {
+ if (attribute != null)
+ return attribute.Type;
+ else if (IsCollectionType(field.FieldType))
+ return ArgumentType.MultipleUnique;
+ else
+ return ArgumentType.AtMostOnce;
+ }
+
+ private static bool IsCollectionType(Type type)
+ {
+ return type.IsArray;
+ }
+
+ private static bool IsValidElementType(Type type)
+ {
+ return type != null && (
+ type == typeof(int) ||
+ type == typeof(uint) ||
+ type == typeof(string) ||
+ type == typeof(bool) ||
+ type.IsEnum);
+ }
+
+ [System.Diagnostics.DebuggerDisplay("Name = {LongName}")]
+ private class Argument
+ {
+ public Argument(ArgumentAttribute attribute, FieldInfo field, ErrorReporter reporter)
+ {
+ this.longName = Parser.LongName(attribute, field);
+ this.explicitShortName = Parser.ExplicitShortName(attribute);
+ this.shortName = Parser.ShortName(attribute, field);
+ this.hasHelpText = Parser.HasHelpText(attribute);
+ this.helpText = Parser.HelpText(attribute, field);
+ this.defaultValue = Parser.DefaultValue(attribute, field);
+ this.elementType = ElementType(field);
+ this.flags = Flags(attribute, field);
+ this.field = field;
+ this.seenValue = false;
+ this.reporter = reporter;
+ this.isDefault = attribute != null && attribute is DefaultArgumentAttribute;
+
+ if (IsCollection)
+ {
+ this.collectionValues = new ArrayList();
+ }
+
+ Debug.Assert(this.longName != null && this.longName != "");
+ Debug.Assert(!this.isDefault || !this.ExplicitShortName);
+ Debug.Assert(!IsCollection || AllowMultiple, "Collection arguments must have allow multiple");
+ Debug.Assert(!Unique || IsCollection, "Unique only applicable to collection arguments");
+ Debug.Assert(IsValidElementType(Type) ||
+ IsCollectionType(Type));
+ Debug.Assert((IsCollection && IsValidElementType(elementType)) ||
+ (!IsCollection && elementType == null));
+ Debug.Assert(!(this.IsRequired && this.HasDefaultValue), "Required arguments cannot have default value");
+ Debug.Assert(!this.HasDefaultValue || (this.defaultValue.GetType() == field.FieldType), "Type of default value must match field type");
+ }
+
+ public bool Finish(object destination)
+ {
+ if (this.SeenValue)
+ {
+ if (this.IsCollection)
+ {
+ this.field.SetValue(destination, this.collectionValues.ToArray(this.elementType));
+ }
+ }
+ else
+ {
+ if (this.HasDefaultValue)
+ {
+ this.field.SetValue(destination, this.DefaultValue);
+ }
+ }
+
+ return ReportMissingRequiredArgument();
+ }
+
+ private bool ReportMissingRequiredArgument()
+ {
+ if (this.IsRequired && !this.SeenValue)
+ {
+ if (this.IsDefault)
+ reporter(string.Format("Missing required argument '<{0}>'.", this.LongName));
+ else
+ reporter(string.Format("Missing required argument '/{0}'.", this.LongName));
+ return true;
+ }
+ return false;
+ }
+
+ private void ReportDuplicateArgumentValue(string value)
+ {
+ this.reporter(string.Format("Duplicate '{0}' argument '{1}'", this.LongName, value));
+ }
+
+ public bool SetValue(string value, object destination)
+ {
+ if (SeenValue && !AllowMultiple)
+ {
+ this.reporter(string.Format("Duplicate '{0}' argument", this.LongName));
+ return false;
+ }
+ this.seenValue = true;
+
+ object newValue;
+ if (!ParseValue(this.ValueType, value, out newValue))
+ return false;
+ if (this.IsCollection)
+ {
+ if (this.Unique && this.collectionValues.Contains(newValue))
+ {
+ ReportDuplicateArgumentValue(value);
+ return false;
+ }
+ else
+ {
+ this.collectionValues.Add(newValue);
+ }
+ }
+ else
+ {
+ this.field.SetValue(destination, newValue);
+ }
+
+ return true;
+ }
+
+ public Type ValueType
+ {
+ get { return this.IsCollection ? this.elementType : this.Type; }
+ }
+
+ private void ReportBadArgumentValue(string value)
+ {
+ this.reporter(string.Format("'{0}' is not a valid value for the '{1}' command line option", value, this.LongName));
+ }
+
+ private bool ParseValue(Type type, string stringData, out object value)
+ {
+ // null is only valid for bool variables
+ // empty string is never valid
+ if ((stringData != null || type == typeof(bool)) && (stringData == null || stringData.Length > 0))
+ {
+ try
+ {
+ if (type == typeof(string))
+ {
+ value = stringData;
+ return true;
+ }
+ else if (type == typeof(bool))
+ {
+ if (stringData == null || stringData == "+")
+ {
+ value = true;
+ return true;
+ }
+ else if (stringData == "-")
+ {
+ value = false;
+ return true;
+ }
+ }
+ else if (type == typeof(int))
+ {
+ value = int.Parse(stringData);
+ return true;
+ }
+ else if (type == typeof(uint))
+ {
+ value = int.Parse(stringData);
+ return true;
+ }
+ else
+ {
+ Debug.Assert(type.IsEnum);
+
+ bool valid = false;
+ foreach (string name in Enum.GetNames(type))
+ {
+ if (name == stringData)
+ {
+ valid = true;
+ break;
+ }
+ }
+ if (valid)
+ {
+ value = Enum.Parse(type, stringData, true);
+ return true;
+ }
+ }
+ }
+ catch
+ {
+ // catch parse errors
+ }
+ }
+
+ ReportBadArgumentValue(stringData);
+ value = null;
+ return false;
+ }
+
+ private void AppendValue(StringBuilder builder, object value)
+ {
+ if (value is string || value is int || value is uint || value.GetType().IsEnum)
+ {
+ builder.Append(value.ToString());
+ }
+ else if (value is bool)
+ {
+ builder.Append((bool) value ? "+" : "-");
+ }
+ else
+ {
+ bool first = true;
+ foreach (object o in (System.Array) value)
+ {
+ if (!first)
+ {
+ builder.Append(", ");
+ }
+ AppendValue(builder, o);
+ first = false;
+ }
+ }
+ }
+
+ public string LongName
+ {
+ get { return this.longName; }
+ }
+
+ public bool ExplicitShortName
+ {
+ get { return this.explicitShortName; }
+ }
+
+ public string ShortName
+ {
+ get
+ {
+ Debug.Assert(this.HasShortName);
+ return this.shortName;
+ }
+ }
+
+ public bool HasShortName
+ {
+ get { return this.shortName != null && this.shortName.Length > 0; }
+ }
+
+ public void ClearShortName()
+ {
+ this.shortName = null;
+ }
+
+ public bool HasHelpText
+ {
+ get { return this.hasHelpText; }
+ }
+
+ public string HelpText
+ {
+ get { return this.helpText; }
+ }
+
+ public object DefaultValue
+ {
+ get { return this.defaultValue; }
+ }
+
+ public bool HasDefaultValue
+ {
+ get { return null != this.defaultValue; }
+ }
+
+ public string FullHelpText
+ {
+ get {
+ StringBuilder builder = new StringBuilder();
+ if (this.HasHelpText)
+ {
+ builder.Append(this.HelpText);
+ }
+ if (this.HasDefaultValue)
+ {
+ if (builder.Length > 0)
+ builder.Append(" ");
+ builder.Append("Default value:'");
+ AppendValue(builder, this.DefaultValue);
+ builder.Append('\'');
+ }
+ if (this.HasShortName)
+ {
+ if (builder.Length > 0)
+ builder.Append(" ");
+ builder.Append("Short form is /");
+ builder.Append(this.ShortName);
+ builder.Append(":");
+ builder.Append(this.ValueTypeString);
+ builder.Append(".");
+ }
+ return builder.ToString();
+ }
+ }
+
+ public string SyntaxHelp
+ {
+ get
+ {
+ StringBuilder builder = new StringBuilder();
+
+ if (this.IsDefault)
+ {
+ builder.Append("<");
+ builder.Append(this.LongName);
+ builder.Append(">");
+ }
+ else
+ {
+ builder.Append("/");
+ builder.Append(this.LongName);
+ builder.Append(":");
+ builder.Append(ValueTypeString);
+ }
+
+ return builder.ToString();
+ }
+ }
+
+ public string ValueTypeString
+ {
+ get
+ {
+ Type valueType = this.ValueType;
+ string type;
+ if (valueType == typeof(int))
+ type = "";
+ else if (valueType == typeof(uint))
+ type = "";
+ else if (valueType == typeof(bool))
+ type = "[+|-]";
+ else if (valueType == typeof(string))
+ type = "";
+ else
+ {
+ Debug.Assert(valueType.IsEnum);
+ var builder = new StringBuilder();
+ builder.Append("{");
+ bool first = true;
+ foreach (FieldInfo field in valueType.GetFields())
+ {
+ if (field.IsStatic)
+ {
+ if (first)
+ first = false;
+ else
+ builder.Append('|');
+ builder.Append(field.Name);
+ }
+ }
+ builder.Append('}');
+ type = builder.ToString();
+ }
+ return type;
+ }
+ }
+
+ public bool IsRequired
+ {
+ get { return 0 != (this.flags & ArgumentType.Required); }
+ }
+
+ public bool SeenValue
+ {
+ get { return this.seenValue; }
+ }
+
+ public bool AllowMultiple
+ {
+ get { return 0 != (this.flags & ArgumentType.Multiple); }
+ }
+
+ public bool Unique
+ {
+ get { return 0 != (this.flags & ArgumentType.Unique); }
+ }
+
+ public Type Type
+ {
+ get { return field.FieldType; }
+ }
+
+ public bool IsCollection
+ {
+ get { return IsCollectionType(Type); }
+ }
+
+ public bool IsDefault
+ {
+ get { return this.isDefault; }
+ }
+
+ private string longName;
+ private string shortName;
+ private string helpText;
+ private bool hasHelpText;
+ private bool explicitShortName;
+ private object defaultValue;
+ private bool seenValue;
+ private FieldInfo field;
+ private Type elementType;
+ private ArgumentType flags;
+ private ArrayList collectionValues;
+ private ErrorReporter reporter;
+ private bool isDefault;
+ }
+
+ private ArrayList arguments;
+ private Hashtable argumentMap;
+ private Argument defaultArgument;
+ private ErrorReporter reporter;
+ }
+}
\ No newline at end of file
diff --git a/CpuToYourEars/CpuToYourEars.csproj b/CpuToYourEars/CpuToYourEars.csproj
new file mode 100644
index 0000000..d58510f
--- /dev/null
+++ b/CpuToYourEars/CpuToYourEars.csproj
@@ -0,0 +1,59 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {BE9E02BB-0939-4AE7-84F4-EDAA78FE6505}
+ Exe
+ AudioMonitor
+ CpuToYourEars
+ v4.6.1
+ 512
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\Dependencies\Sanford.Multimedia.dll
+
+
+ False
+ ..\Dependencies\Sanford.Multimedia.Midi.dll
+
+
+ ..\packages\ShellProgress.1.0.0.0\lib\net45\ShellProgress.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CpuToYourEars/Program.cs b/CpuToYourEars/Program.cs
new file mode 100644
index 0000000..ea4a05a
--- /dev/null
+++ b/CpuToYourEars/Program.cs
@@ -0,0 +1,343 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Principal;
+using System.Threading;
+using Microsoft.CommandLineHelper;
+using Sanford.Multimedia.Midi;
+using ShellProgress;
+
+namespace AudioMonitor
+{
+ public enum Notes
+ {
+ C = 60,
+ Cs = 61,
+ D = 62,
+ Ds = 63,
+ E = 64,
+ F = 65,
+ Fs = 66,
+ G = 67,
+ Gs = 68,
+ A = 69,
+ As = 70,
+ B = 71
+ }
+
+ public class Program
+ {
+ #region Private Variables
+
+ private static OutputDevice outDevice;
+ private static PerformanceCounter cpuCounter;
+ private static Timer tmrCheckPerfCounter;
+
+ private static readonly Chord Cmajor = new Chord(Notes.C, Notes.E, Notes.G); // c major
+ private static readonly Chord Dmajor = new Chord(Notes.D, Notes.Fs, Notes.A); // d major
+ private static readonly Chord Emajor = new Chord(Notes.E, Notes.Gs, Notes.B); // e major
+ private static readonly Chord Fmajor = new Chord(Notes.F, Notes.A, Notes.C); // f major
+ private static readonly Chord Gmajor = new Chord(Notes.G, Notes.B, Notes.D); // g major
+ private static readonly Chord Amajor = new Chord(Notes.A, Notes.Cs, Notes.E); // a major
+ private static readonly Chord Bmajor = new Chord(Notes.B, Notes.Ds, Notes.Fs); // b major
+
+ private static Chord lastChord = null;
+ private static int lastNoteOffset = 0;
+
+ private static readonly Arguments appArgs = new Arguments();
+ private static WindowsImpersonationContext impersonationContext = null;
+ private static IProgressing appProgressBar = null;
+
+ [DllImport("advapi32.dll")]
+ public static extern bool LogonUser(string name, string domain, string pass, int logType, int logpv, out IntPtr pht);
+
+ #endregion
+
+ static void Main(string[] args)
+ {
+ if (!Parser.ParseArgumentsWithUsage(args, appArgs))
+ Environment.Exit(-1);
+
+ try
+ {
+ AskForPassword();
+ SetPerfCounter();
+ if (!SetupMidiOutputDevice()) return;
+ SetTimer();
+ SetupProgressBar();
+ SetupWaitingToExit();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
+ }
+
+ #region Setup Application
+
+ private static void AskForPassword()
+ {
+ // Ask for password if there is a passed in Domain Login, but no password
+ if (string.IsNullOrEmpty(appArgs.DomainLogin) || !string.IsNullOrEmpty(appArgs.Password)) return;
+
+ string pass = "";
+ Console.Write("Enter your password: ");
+ ConsoleKeyInfo key;
+
+ do
+ {
+ key = Console.ReadKey(true);
+
+ // Backspace Should Not Work
+ if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
+ {
+ pass += key.KeyChar;
+ Console.Write("*");
+ }
+ else
+ {
+ if (key.Key == ConsoleKey.Backspace && pass.Length > 0)
+ {
+ pass = pass.Substring(0, (pass.Length - 1));
+ Console.Write("\b \b");
+ }
+ }
+ }
+ // Stops Receving Keys Once Enter is Pressed
+ while (key.Key != ConsoleKey.Enter);
+
+ appArgs.Password = pass;
+ Console.WriteLine();
+ }
+
+ private static void SetPerfCounter()
+ {
+ Console.WriteLine("Connecting to Perfmon Counter...");
+ if (appArgs.DomainLogin.Length > 0)
+ {
+ LogonUser(appArgs.Login, appArgs.Domain, appArgs.Password, 9, 0, out var ptr);
+ WindowsIdentity windowsIdentity = new WindowsIdentity(ptr);
+ impersonationContext = windowsIdentity.Impersonate();
+ }
+
+ cpuCounter = string.IsNullOrEmpty(appArgs.Machine) ?
+ new PerformanceCounter("Processor", "% Processor Time", "_Total") :
+ new PerformanceCounter("Processor", "% Processor Time", "_Total", appArgs.Machine);
+ }
+
+ private static bool SetupMidiOutputDevice()
+ {
+ if (OutputDevice.DeviceCount == 0)
+ {
+ Console.WriteLine("No MIDI output devices available.");
+ return false;
+ }
+ else
+ {
+ try
+ {
+ int outDeviceID = 0;
+ outDevice = new OutputDevice(outDeviceID);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static void SetTimer()
+ {
+ tmrCheckPerfCounter = new Timer(CheckPerfCounter, null, 100, 500);
+ }
+
+ private static void SetupProgressBar()
+ {
+ var factory = new ProgressBarFactory();
+ appProgressBar = factory.CreateInstance(100);
+ }
+
+ private static void SetupWaitingToExit()
+ {
+ Console.WriteLine("Press ESC to stop");
+ while (Console.ReadKey(true).Key == ConsoleKey.Escape)
+ {
+ ReleaseResources();
+ Environment.Exit(0);
+ }
+ }
+
+ #endregion
+
+ #region Helper Methods
+
+ private static void CheckPerfCounter(object state)
+ {
+ PlayBasedOnPerfCounter();
+ }
+
+ private static void PlayBasedOnPerfCounter()
+ {
+ float value = cpuCounter.NextValue();
+ Debug.WriteLine(value);
+
+ appProgressBar.Update((int) value);
+
+ StopLastChord();
+
+ ChordToPlayBasedOnValue(value, out var chordToPlay, out var octaveToPlay);
+ PlayChord(chordToPlay, octaveToPlay);
+ }
+
+ private static void ChordToPlayBasedOnValue(float value, out Chord chordToPlay, out int octaveToPlay)
+ {
+ octaveToPlay = 0; // set default
+
+ if (value >= 0 && value < 10)
+ {
+ chordToPlay = Cmajor;
+ octaveToPlay = -2;
+ }
+ else if (value >= 10 && value < 20)
+ {
+ chordToPlay = Dmajor;
+ octaveToPlay = -1;
+ }
+ else if (value >= 20 && value < 30)
+ {
+ chordToPlay = Emajor;
+ octaveToPlay = -1;
+ }
+ else if (value >= 30 && value < 40)
+ {
+ chordToPlay = Fmajor;
+ }
+ else if (value >= 40 && value < 50)
+ {
+ chordToPlay = Gmajor;
+ }
+ else if (value >= 50 && value < 60)
+ {
+ chordToPlay = Amajor;
+ }
+ else if (value >= 60 && value < 70)
+ {
+ chordToPlay = Bmajor;
+ }
+ else if (value >= 70 && value < 80)
+ {
+ chordToPlay = Cmajor;
+ octaveToPlay = 1;
+ }
+ else if (value >= 80 && value < 90)
+ {
+ chordToPlay = Dmajor;
+ octaveToPlay = 2;
+ }
+ else
+ {
+ chordToPlay = Emajor;
+ octaveToPlay = 2;
+ }
+ }
+
+ private static void PlayChord(Chord chord, int octave)
+ {
+ int noteOffset = (octave * 12);
+ outDevice.Send(new ChannelMessage(ChannelCommand.NoteOn, 0, (int)chord.Note1 + noteOffset, 127));
+ outDevice.Send(new ChannelMessage(ChannelCommand.NoteOn, 0, (int)chord.Note2 + noteOffset, 127));
+ outDevice.Send(new ChannelMessage(ChannelCommand.NoteOn, 0, (int)chord.Note3 + noteOffset, 127));
+
+ lastChord = chord;
+ lastNoteOffset = noteOffset;
+ }
+
+ private static void StopLastChord()
+ {
+ if (lastChord != null)
+ {
+ outDevice.Send(new ChannelMessage(ChannelCommand.NoteOff, 0, (int)lastChord.Note1 + lastNoteOffset, 0));
+ outDevice.Send(new ChannelMessage(ChannelCommand.NoteOff, 0, (int)lastChord.Note2 + lastNoteOffset, 0));
+ outDevice.Send(new ChannelMessage(ChannelCommand.NoteOff, 0, (int)lastChord.Note3 + lastNoteOffset, 0));
+ }
+ }
+
+ #endregion
+
+ #region Application Cleanup
+
+ private static void ReleaseResources()
+ {
+ outDevice?.Dispose();
+
+ try
+ {
+ impersonationContext?.Undo();
+ impersonationContext?.Dispose();
+ }
+ catch
+ {
+ // nothing we can do
+ }
+ }
+
+ #endregion
+ }
+
+ [CommandLineArguments(CaseSensitive = false)]
+ public class Arguments
+ {
+ [Argument(ArgumentType.AtMostOnce, HelpText = "Machine Name", DefaultValue = "", LongName = "Machine", ShortName = "m")]
+ public string Machine;
+ [Argument(ArgumentType.AtMostOnce, HelpText = @"Domain Login, e.g CompanyDomain\username", DefaultValue = "", LongName = "DomainLogin", ShortName = "dl")]
+ public string DomainLogin;
+ [Argument(ArgumentType.AtMostOnce, HelpText = "Password", DefaultValue = "", LongName = "Password", ShortName = "p")]
+ public string Password;
+
+ // helper properties
+ public string Domain
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(DomainLogin)) return "";
+
+ var split = DomainLogin.Split(@"\".ToCharArray());
+ if (split.Length == 2)
+ return split[0];
+
+ return "";
+ }
+ }
+
+ public string Login {
+ get
+ {
+ if (string.IsNullOrEmpty(DomainLogin)) return "";
+
+ var split = DomainLogin.Split(@"\".ToCharArray());
+ if (split.Length == 2)
+ return split[1];
+
+ return "";
+ }
+ }
+ }
+
+ public class Chord
+ {
+ public Chord() {}
+ public Chord(Notes note1, Notes note2, Notes note3)
+ {
+ this.Note1 = note1;
+ this.Note2 = note2;
+ this.Note3 = note3;
+ }
+ public Notes Note1 { get; set; }
+ public Notes Note2 { get; set; }
+ public Notes Note3 { get; set; }
+ }
+}
diff --git a/CpuToYourEars/Properties/AssemblyInfo.cs b/CpuToYourEars/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..160a77f
--- /dev/null
+++ b/CpuToYourEars/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AudioMonitor")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("HP Inc.")]
+[assembly: AssemblyProduct("AudioMonitor")]
+[assembly: AssemblyCopyright("Copyright © HP Inc. 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("be9e02bb-0939-4ae7-84f4-edaa78fe6505")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CpuToYourEars/packages.config b/CpuToYourEars/packages.config
new file mode 100644
index 0000000..3d8bf01
--- /dev/null
+++ b/CpuToYourEars/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Dependencies/Sanford.Collections.dll b/Dependencies/Sanford.Collections.dll
new file mode 100644
index 0000000..43aaa54
Binary files /dev/null and b/Dependencies/Sanford.Collections.dll differ
diff --git a/Dependencies/Sanford.Multimedia.Midi.dll b/Dependencies/Sanford.Multimedia.Midi.dll
new file mode 100644
index 0000000..9f0f7d9
Binary files /dev/null and b/Dependencies/Sanford.Multimedia.Midi.dll differ
diff --git a/Dependencies/Sanford.Multimedia.Timers.dll b/Dependencies/Sanford.Multimedia.Timers.dll
new file mode 100644
index 0000000..bf91281
Binary files /dev/null and b/Dependencies/Sanford.Multimedia.Timers.dll differ
diff --git a/Dependencies/Sanford.Multimedia.dll b/Dependencies/Sanford.Multimedia.dll
new file mode 100644
index 0000000..d5a90a8
Binary files /dev/null and b/Dependencies/Sanford.Multimedia.dll differ
diff --git a/Dependencies/Sanford.Threading.dll b/Dependencies/Sanford.Threading.dll
new file mode 100644
index 0000000..e688f20
Binary files /dev/null and b/Dependencies/Sanford.Threading.dll differ
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/AssemblyInfo.cs b/DependenciesCode/Sanford.Multimedia.Midi/AssemblyInfo.cs
new file mode 100644
index 0000000..aa080fc
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/AssemblyInfo.cs
@@ -0,0 +1,61 @@
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+//
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+//
+[assembly: AssemblyTitle("MIDI Toolkit")]
+[assembly: AssemblyDescription("A toolkit for creating MIDI applications.")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Leslie Sanford 2006")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+//
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("5.0.0.0")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+// (*) If no key is specified, the assembly is not signed.
+// (*) KeyName refers to a key that has been installed in the Crypto Service
+// Provider (CSP) on your machine. KeyFile refers to a file which contains
+// a key.
+// (*) If the KeyFile and the KeyName values are both specified, the
+// following processing occurs:
+// (1) If the KeyName can be found in the CSP, that key is used.
+// (2) If the KeyName does not exist and the KeyFile does exist, the key
+// in the KeyFile is installed into the CSP and used.
+// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+// When specifying the KeyFile, the location of the KeyFile should be
+// relative to the project output directory which is
+// %Project Directory%\obj\. For example, if your KeyFile is
+// located in the project directory, you would specify the AssemblyKeyFile
+// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
+// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+// documentation for more information on this.
+//
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
+[assembly: AssemblyKeyName("")]
+
+[assembly: CLSCompliant(true)]
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Clocks/IClock.cs b/DependenciesCode/Sanford.Multimedia.Midi/Clocks/IClock.cs
new file mode 100644
index 0000000..049fc53
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Clocks/IClock.cs
@@ -0,0 +1,89 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Represents functionality for generating events for driving Sequence playback.
+ ///
+ public interface IClock
+ {
+ #region IClock Members
+
+ ///
+ /// Occurs when an IClock generates a tick.
+ ///
+ event EventHandler Tick;
+
+ ///
+ /// Occurs when an IClock starts generating Ticks.
+ ///
+ ///
+ /// When an IClock is started, it resets itself and generates ticks to
+ /// drive playback from the beginning of the Sequence.
+ ///
+ event EventHandler Started;
+
+ ///
+ /// Occurs when an IClock continues generating Ticks.
+ ///
+ ///
+ /// When an IClock is continued, it generates ticks to drive playback
+ /// from the current position within the Sequence.
+ ///
+ event EventHandler Continued;
+
+ ///
+ /// Occurs when an IClock is stopped.
+ ///
+ event EventHandler Stopped;
+
+ ///
+ /// Gets a value indicating whether the IClock is running.
+ ///
+ bool IsRunning
+ {
+ get;
+ }
+
+ int Ticks
+ {
+ get;
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Clocks/MidiInternalClock.cs b/DependenciesCode/Sanford.Multimedia.Midi/Clocks/MidiInternalClock.cs
new file mode 100644
index 0000000..bcc054d
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Clocks/MidiInternalClock.cs
@@ -0,0 +1,384 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using Sanford.Multimedia.Timers;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Generates clock events internally.
+ ///
+ public class MidiInternalClock : PpqnClock, IComponent
+ {
+ #region MidiInternalClock Members
+
+ #region Fields
+
+ // Used for generating tick events.
+ private Timer timer = new Timer();
+
+ // Parses meta message tempo change messages.
+ private TempoChangeBuilder builder = new TempoChangeBuilder();
+
+ // Tick accumulator.
+ private int ticks = 0;
+
+ // Indicates whether the clock has been disposed.
+ private bool disposed = false;
+
+ private ISite site = null;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the MidiInternalClock class.
+ ///
+ public MidiInternalClock() : base(Timer.Capabilities.periodMin)
+ {
+ timer.Period = Timer.Capabilities.periodMin;
+ timer.Tick += new EventHandler(HandleTick);
+ }
+
+ public MidiInternalClock(int timerPeriod) : base(timerPeriod)
+ {
+ timer.Period = timerPeriod;
+ timer.Tick += new EventHandler(HandleTick);
+ }
+
+ ///
+ /// Initializes a new instance of the MidiInternalClock class with the
+ /// specified IContainer.
+ ///
+ ///
+ /// The IContainer to which the MidiInternalClock will add itself.
+ ///
+ public MidiInternalClock(IContainer container) :
+ base(Timer.Capabilities.periodMin)
+ {
+ ///
+ /// Required for Windows.Forms Class Composition Designer support
+ ///
+ container.Add(this);
+
+ timer.Period = Timer.Capabilities.periodMin;
+ timer.Tick += new EventHandler(HandleTick);
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Starts the MidiInternalClock.
+ ///
+ public void Start()
+ {
+ #region Require
+
+ if(disposed)
+ {
+ throw new ObjectDisposedException("MidiInternalClock");
+ }
+
+ #endregion
+
+ #region Guard
+
+ if(running)
+ {
+ return;
+ }
+
+ #endregion
+
+ ticks = 0;
+
+ Reset();
+
+ OnStarted(EventArgs.Empty);
+
+ // Start the multimedia timer in order to start generating ticks.
+ timer.Start();
+
+ // Indicate that the clock is now running.
+ running = true;
+
+ }
+
+ ///
+ /// Resumes tick generation from the current position.
+ ///
+ public void Continue()
+ {
+ #region Require
+
+ if(disposed)
+ {
+ throw new ObjectDisposedException("MidiInternalClock");
+ }
+
+ #endregion
+
+ #region Guard
+
+ if(running)
+ {
+ return;
+ }
+
+ #endregion
+
+ // Raise Continued event.
+ OnContinued(EventArgs.Empty);
+
+ // Start multimedia timer in order to start generating ticks.
+ timer.Start();
+
+ // Indicate that the clock is now running.
+ running = true;
+ }
+
+ ///
+ /// Stops the MidiInternalClock.
+ ///
+ public void Stop()
+ {
+ #region Require
+
+ if(disposed)
+ {
+ throw new ObjectDisposedException("MidiInternalClock");
+ }
+
+ #endregion
+
+ #region Guard
+
+ if(!running)
+ {
+ return;
+ }
+
+ #endregion
+
+ // Stop the multimedia timer.
+ timer.Stop();
+
+ // Indicate that the clock is not running.
+ running = false;
+
+ OnStopped(EventArgs.Empty);
+ }
+
+ public void SetTicks(int ticks)
+ {
+ #region Require
+
+ if(ticks < 0)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ #endregion
+
+ if(IsRunning)
+ {
+ Stop();
+ }
+
+ this.ticks = ticks;
+
+ Reset();
+ }
+
+ public void Process(MetaMessage message)
+ {
+ #region Require
+
+ if(message == null)
+ {
+ throw new ArgumentNullException("message");
+ }
+
+ #endregion
+
+ #region Guard
+
+ if(message.MetaType != MetaType.Tempo)
+ {
+ return;
+ }
+
+ #endregion
+
+ TempoChangeBuilder builder = new TempoChangeBuilder(message);
+
+ // Set the new tempo.
+ Tempo = builder.Tempo;
+ }
+
+ #region Event Raiser Methods
+
+ protected virtual void OnDisposed(EventArgs e)
+ {
+ EventHandler handler = Disposed;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ #endregion
+
+ #region Event Handler Methods
+
+ // Handles Tick events generated by the multimedia timer.
+ private void HandleTick(object sender, EventArgs e)
+ {
+ int t = GenerateTicks();
+
+ for(int i = 0; i < t; i++)
+ {
+ OnTick(EventArgs.Empty);
+
+ ticks++;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets or sets the tempo in microseconds per beat.
+ ///
+ public int Tempo
+ {
+ get
+ {
+ #region Require
+
+ if(disposed)
+ {
+ throw new ObjectDisposedException("MidiInternalClock");
+ }
+
+ #endregion
+
+ return GetTempo();
+ }
+ set
+ {
+ #region Require
+
+ if(disposed)
+ {
+ throw new ObjectDisposedException("MidiInternalClock");
+ }
+
+ #endregion
+
+ SetTempo(value);
+ }
+ }
+
+ public override int Ticks
+ {
+ get
+ {
+ return ticks;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IComponent Members
+
+ public event EventHandler Disposed;
+
+ public ISite Site
+ {
+ get
+ {
+ return site;
+ }
+ set
+ {
+ site = value;
+ }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ #region Guard
+
+ if(disposed)
+ {
+ return;
+ }
+
+ #endregion
+
+ if(running)
+ {
+ // Stop the multimedia timer.
+ timer.Stop();
+ }
+
+ disposed = true;
+
+ timer.Dispose();
+
+ GC.SuppressFinalize(this);
+
+ OnDisposed(EventArgs.Empty);
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Clocks/PpqnClock.cs b/DependenciesCode/Sanford.Multimedia.Midi/Clocks/PpqnClock.cs
new file mode 100644
index 0000000..bda7f9c
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Clocks/PpqnClock.cs
@@ -0,0 +1,264 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Provides basic functionality for generating tick events with pulses per
+ /// quarter note resolution.
+ ///
+ public abstract class PpqnClock : IClock
+ {
+ #region PpqnClock Members
+
+ #region Fields
+
+ ///
+ /// The default tempo in microseconds: 120bpm.
+ ///
+ public const int DefaultTempo = 500000;
+
+ ///
+ /// The minimum pulses per quarter note value.
+ ///
+ public const int PpqnMinValue = 24;
+
+ // The number of microseconds per millisecond.
+ private const int MicrosecondsPerMillisecond = 1000;
+
+ // The pulses per quarter note value.
+ private int ppqn = PpqnMinValue;
+
+ // The tempo in microseconds.
+ private int tempo = DefaultTempo;
+
+ // The product of the timer period, the pulses per quarter note, and
+ // the number of microseconds per millisecond.
+ private int periodResolution;
+
+ // The number of ticks per MIDI clock.
+ private int ticksPerClock;
+
+ // The running fractional tick count.
+ private int fractionalTicks = 0;
+
+ // The timer period.
+ private readonly int timerPeriod;
+
+ // Indicates whether the clock is running.
+ protected bool running = false;
+
+ #endregion
+
+ #region Construction
+
+ protected PpqnClock(int timerPeriod)
+ {
+ #region Require
+
+ if(timerPeriod < 1)
+ {
+ throw new ArgumentOutOfRangeException("timerPeriod", timerPeriod,
+ "Timer period cannot be less than one.");
+ }
+
+ #endregion
+
+ this.timerPeriod = timerPeriod;
+
+ CalculatePeriodResolution();
+ CalculateTicksPerClock();
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected int GetTempo()
+ {
+ return tempo;
+ }
+
+ protected void SetTempo(int tempo)
+ {
+ #region Require
+
+ if(tempo < 1)
+ {
+ throw new ArgumentOutOfRangeException(
+ "Tempo out of range.");
+ }
+
+ #endregion
+
+ this.tempo = tempo;
+ }
+
+ protected void Reset()
+ {
+ fractionalTicks = 0;
+ }
+
+ protected int GenerateTicks()
+ {
+ int ticks = (fractionalTicks + periodResolution) / tempo;
+ fractionalTicks += periodResolution - ticks * tempo;
+
+ return ticks;
+ }
+
+ private void CalculatePeriodResolution()
+ {
+ periodResolution = ppqn * timerPeriod * MicrosecondsPerMillisecond;
+ }
+
+ private void CalculateTicksPerClock()
+ {
+ ticksPerClock = ppqn / PpqnMinValue;
+ }
+
+ protected virtual void OnTick(EventArgs e)
+ {
+ EventHandler handler = Tick;
+
+ if(handler != null)
+ {
+ handler(this, EventArgs.Empty);
+ }
+ }
+
+ protected virtual void OnStarted(EventArgs e)
+ {
+ EventHandler handler = Started;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ protected virtual void OnStopped(EventArgs e)
+ {
+ EventHandler handler = Stopped;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ protected virtual void OnContinued(EventArgs e)
+ {
+ EventHandler handler = Continued;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ #endregion
+
+ #region Properties
+
+ public int Ppqn
+ {
+ get
+ {
+ return ppqn;
+ }
+ set
+ {
+ #region Require
+
+ if(value < PpqnMinValue)
+ {
+ throw new ArgumentOutOfRangeException("Ppqn", value,
+ "Pulses per quarter note out of range.");
+ }
+ else if(value % PpqnMinValue != 0)
+ {
+ throw new ArgumentException(
+ "Pulses per quarter note is not a multiple of 24.");
+ }
+
+ #endregion
+
+ ppqn = value;
+
+ CalculatePeriodResolution();
+ CalculateTicksPerClock();
+ }
+ }
+
+ public abstract int Ticks
+ {
+ get;
+ }
+
+ public int TicksPerClock
+ {
+ get
+ {
+ return ticksPerClock;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IClock Members
+
+ public event System.EventHandler Tick;
+
+ public event System.EventHandler Started;
+
+ public event System.EventHandler Continued;
+
+ public event System.EventHandler Stopped;
+
+ public bool IsRunning
+ {
+ get
+ {
+ return running;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Construction.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Construction.cs
new file mode 100644
index 0000000..ca6791f
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Construction.cs
@@ -0,0 +1,75 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System.Threading;
+using Sanford.Threading;
+
+namespace Sanford.Multimedia.Midi
+{
+ public partial class InputDevice : MidiDevice
+ {
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the InputDevice class with the
+ /// specified device ID.
+ ///
+ public InputDevice(int deviceID) : base(deviceID)
+ {
+ midiInProc = HandleMessage;
+
+ int result = midiInOpen(ref handle, deviceID, midiInProc, 0, CALLBACK_FUNCTION);
+
+ if(result == MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ delegateQueue = new DelegateQueue();
+ }
+ else
+ {
+ throw new InputDeviceException(result);
+ }
+ }
+
+ ~InputDevice()
+ {
+ if(!IsDisposed)
+ {
+ midiInReset(Handle);
+ midiInClose(Handle);
+ }
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Events.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Events.cs
new file mode 100644
index 0000000..0e1a377
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Events.cs
@@ -0,0 +1,97 @@
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ public partial class InputDevice
+ {
+ public event EventHandler ChannelMessageReceived;
+
+ public event EventHandler SysExMessageReceived;
+
+ public event EventHandler SysCommonMessageReceived;
+
+ public event EventHandler SysRealtimeMessageReceived;
+
+ public event EventHandler InvalidShortMessageReceived;
+
+ public event EventHandler InvalidSysExMessageReceived;
+
+ protected virtual void OnChannelMessageReceived(ChannelMessageEventArgs e)
+ {
+ EventHandler handler = ChannelMessageReceived;
+
+ if(handler != null)
+ {
+ context.Post(delegate(object dummy)
+ {
+ handler(this, e);
+ }, null);
+ }
+ }
+
+ protected virtual void OnSysExMessageReceived(SysExMessageEventArgs e)
+ {
+ EventHandler handler = SysExMessageReceived;
+
+ if(handler != null)
+ {
+ context.Post(delegate(object dummy)
+ {
+ handler(this, e);
+ }, null);
+ }
+ }
+
+ protected virtual void OnSysCommonMessageReceived(SysCommonMessageEventArgs e)
+ {
+ EventHandler handler = SysCommonMessageReceived;
+
+ if(handler != null)
+ {
+ context.Post(delegate(object dummy)
+ {
+ handler(this, e);
+ }, null);
+ }
+ }
+
+ protected virtual void OnSysRealtimeMessageReceived(SysRealtimeMessageEventArgs e)
+ {
+ EventHandler handler = SysRealtimeMessageReceived;
+
+ if(handler != null)
+ {
+ context.Post(delegate(object dummy)
+ {
+ handler(this, e);
+ }, null);
+ }
+ }
+
+ protected virtual void OnInvalidShortMessageReceived(InvalidShortMessageEventArgs e)
+ {
+ EventHandler handler = InvalidShortMessageReceived;
+
+ if(handler != null)
+ {
+ context.Post(delegate(object dummy)
+ {
+ handler(this, e);
+ }, null);
+ }
+ }
+
+ protected virtual void OnInvalidSysExMessageReceived(InvalidSysExMessageEventArgs e)
+ {
+ EventHandler handler = InvalidSysExMessageReceived;
+
+ if(handler != null)
+ {
+ context.Post(delegate(object dummy)
+ {
+ handler(this, e);
+ }, null);
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Fields.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Fields.cs
new file mode 100644
index 0000000..fd618ab
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Fields.cs
@@ -0,0 +1,71 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Threading;
+using Sanford.Threading;
+
+namespace Sanford.Multimedia.Midi
+{
+ public partial class InputDevice
+ {
+ private delegate void GenericDelegate(T args);
+
+ private DelegateQueue delegateQueue = null;
+
+ private volatile int bufferCount = 0;
+
+ private readonly object lockObject = new object();
+
+ private MidiInProc midiInProc;
+
+ private bool recording = false;
+
+ private MidiHeaderBuilder headerBuilder = new MidiHeaderBuilder();
+
+ private ChannelMessageBuilder cmBuilder = new ChannelMessageBuilder();
+
+ private SysCommonMessageBuilder scBuilder = new SysCommonMessageBuilder();
+
+ private int handle = 0;
+
+ private volatile bool resetting = false;
+
+ private int sysExBufferSize = 4096;
+
+ private List sysExData = new List();
+ }
+}
\ No newline at end of file
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Messaging.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Messaging.cs
new file mode 100644
index 0000000..704d3db
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Messaging.cs
@@ -0,0 +1,277 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Sanford.Multimedia;
+
+namespace Sanford.Multimedia.Midi
+{
+ public partial class InputDevice : MidiDevice
+ {
+ private void HandleMessage(int handle, int msg, int instance, int param1, int param2)
+ {
+ if(msg == MIM_OPEN)
+ {
+ }
+ else if(msg == MIM_CLOSE)
+ {
+ }
+ else if(msg == MIM_DATA)
+ {
+ delegateQueue.Post(HandleShortMessage, param1);
+ }
+ else if(msg == MIM_MOREDATA)
+ {
+ delegateQueue.Post(HandleShortMessage, param1);
+ }
+ else if(msg == MIM_LONGDATA)
+ {
+ delegateQueue.Post(HandleSysExMessage, new IntPtr(param1));
+ }
+ else if(msg == MIM_ERROR)
+ {
+ delegateQueue.Post(HandleInvalidShortMessage, param1);
+ }
+ else if(msg == MIM_LONGERROR)
+ {
+ delegateQueue.Post(HandleInvalidSysExMessage, new IntPtr(param1));
+ }
+ }
+
+ private void HandleShortMessage(object state)
+ {
+ int message = (int)state;
+ int status = ShortMessage.UnpackStatus(message);
+
+ if(status >= (int)ChannelCommand.NoteOff &&
+ status <= (int)ChannelCommand.PitchWheel +
+ ChannelMessage.MidiChannelMaxValue)
+ {
+ cmBuilder.Message = message;
+ cmBuilder.Build();
+
+ OnChannelMessageReceived(new ChannelMessageEventArgs(cmBuilder.Result));
+ }
+ else if(status == (int)SysCommonType.MidiTimeCode ||
+ status == (int)SysCommonType.SongPositionPointer ||
+ status == (int)SysCommonType.SongSelect ||
+ status == (int)SysCommonType.TuneRequest)
+ {
+ scBuilder.Message = message;
+ scBuilder.Build();
+
+ OnSysCommonMessageReceived(new SysCommonMessageEventArgs(scBuilder.Result));
+ }
+ else
+ {
+ SysRealtimeMessageEventArgs e = null;
+
+ switch((SysRealtimeType)status)
+ {
+ case SysRealtimeType.ActiveSense:
+ e = SysRealtimeMessageEventArgs.ActiveSense;
+ break;
+
+ case SysRealtimeType.Clock:
+ e = SysRealtimeMessageEventArgs.Clock;
+ break;
+
+ case SysRealtimeType.Continue:
+ e = SysRealtimeMessageEventArgs.Continue;
+ break;
+
+ case SysRealtimeType.Reset:
+ e = SysRealtimeMessageEventArgs.Reset;
+ break;
+
+ case SysRealtimeType.Start:
+ e = SysRealtimeMessageEventArgs.Start;
+ break;
+
+ case SysRealtimeType.Stop:
+ e = SysRealtimeMessageEventArgs.Stop;
+ break;
+
+ case SysRealtimeType.Tick:
+ e = SysRealtimeMessageEventArgs.Tick;
+ break;
+ }
+
+ OnSysRealtimeMessageReceived(e);
+ }
+ }
+
+ private void HandleSysExMessage(object state)
+ {
+ lock(lockObject)
+ {
+ IntPtr headerPtr = (IntPtr)state;
+
+ MidiHeader header = (MidiHeader)Marshal.PtrToStructure(headerPtr, typeof(MidiHeader));
+
+ if(!resetting)
+ {
+ for(int i = 0; i < header.bytesRecorded; i++)
+ {
+ sysExData.Add(Marshal.ReadByte(header.data, i));
+ }
+
+ if(sysExData[0] == 0xF0 && sysExData[sysExData.Count - 1] == 0xF7)
+ {
+ SysExMessage message = new SysExMessage(sysExData.ToArray());
+
+ sysExData.Clear();
+
+ OnSysExMessageReceived(new SysExMessageEventArgs(message));
+ }
+
+ int result = AddSysExBuffer();
+
+ if(result != DeviceException.MMSYSERR_NOERROR)
+ {
+ Exception ex = new InputDeviceException(result);
+
+ OnError(new ErrorEventArgs(ex));
+ }
+ }
+
+ ReleaseBuffer(headerPtr);
+ }
+ }
+
+ private void HandleInvalidShortMessage(object state)
+ {
+ OnInvalidShortMessageReceived(new InvalidShortMessageEventArgs((int)state));
+ }
+
+ private void HandleInvalidSysExMessage(object state)
+ {
+ lock(lockObject)
+ {
+ IntPtr headerPtr = (IntPtr)state;
+
+ MidiHeader header = (MidiHeader)Marshal.PtrToStructure(headerPtr, typeof(MidiHeader));
+
+ if(!resetting)
+ {
+ byte[] data = new byte[header.bytesRecorded];
+
+ Marshal.Copy(header.data, data, 0, data.Length);
+
+ OnInvalidSysExMessageReceived(new InvalidSysExMessageEventArgs(data));
+
+ int result = AddSysExBuffer();
+
+ if(result != DeviceException.MMSYSERR_NOERROR)
+ {
+ Exception ex = new InputDeviceException(result);
+
+ OnError(new ErrorEventArgs(ex));
+ }
+ }
+
+ ReleaseBuffer(headerPtr);
+ }
+ }
+
+ private void ReleaseBuffer(IntPtr headerPtr)
+ {
+ int result = midiInUnprepareHeader(Handle, headerPtr, SizeOfMidiHeader);
+
+ if(result != DeviceException.MMSYSERR_NOERROR)
+ {
+ Exception ex = new InputDeviceException(result);
+
+ OnError(new ErrorEventArgs(ex));
+ }
+
+ headerBuilder.Destroy(headerPtr);
+
+ bufferCount--;
+
+ Debug.Assert(bufferCount >= 0);
+
+ Monitor.Pulse(lockObject);
+ }
+
+ public int AddSysExBuffer()
+ {
+ int result;
+
+ // Initialize the MidiHeader builder.
+ headerBuilder.BufferLength = sysExBufferSize;
+ headerBuilder.Build();
+
+ // Get the pointer to the built MidiHeader.
+ IntPtr headerPtr = headerBuilder.Result;
+
+ // Prepare the header to be used.
+ result = midiInPrepareHeader(Handle, headerPtr, SizeOfMidiHeader);
+
+ // If the header was perpared successfully.
+ if(result == DeviceException.MMSYSERR_NOERROR)
+ {
+ bufferCount++;
+
+ // Add the buffer to the InputDevice.
+ result = midiInAddBuffer(Handle, headerPtr, SizeOfMidiHeader);
+
+ // If the buffer could not be added.
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ // Unprepare header - there's a chance that this will fail
+ // for whatever reason, but there's not a lot that can be
+ // done at this point.
+ midiInUnprepareHeader(Handle, headerPtr, SizeOfMidiHeader);
+
+ bufferCount--;
+
+ // Destroy header.
+ headerBuilder.Destroy();
+ }
+ }
+ // Else the header could not be prepared.
+ else
+ {
+ // Destroy header.
+ headerBuilder.Destroy();
+ }
+
+ return result;
+ }
+ }
+}
\ No newline at end of file
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Properties.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Properties.cs
new file mode 100644
index 0000000..9fa9560
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Properties.cs
@@ -0,0 +1,79 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.ComponentModel;
+
+namespace Sanford.Multimedia.Midi
+{
+ public partial class InputDevice
+ {
+ public override int Handle
+ {
+ get
+ {
+ return handle;
+ }
+ }
+
+ public int SysExBufferSize
+ {
+ get
+ {
+ return sysExBufferSize;
+ }
+ set
+ {
+ #region Require
+
+ if(value < 1)
+ {
+ throw new ArgumentOutOfRangeException();
+ }
+
+ #endregion
+
+ sysExBufferSize = value;
+ }
+ }
+
+ public static int DeviceCount
+ {
+ get
+ {
+ return midiInGetNumDevs();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.PublicMethods.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.PublicMethods.cs
new file mode 100644
index 0000000..8e86d28
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.PublicMethods.cs
@@ -0,0 +1,213 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.Threading;
+
+namespace Sanford.Multimedia.Midi
+{
+ public partial class InputDevice
+ {
+ public override void Close()
+ {
+ #region Guard
+
+ if(IsDisposed)
+ {
+ return;
+ }
+
+ #endregion
+
+ Dispose(true);
+ }
+
+ public void StartRecording()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("InputDevice");
+ }
+
+ #endregion
+
+ #region Guard
+
+ if(recording)
+ {
+ return;
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ int result = AddSysExBuffer();
+
+ if(result == DeviceException.MMSYSERR_NOERROR)
+ {
+ result = AddSysExBuffer();
+ }
+
+ if(result == DeviceException.MMSYSERR_NOERROR)
+ {
+ result = AddSysExBuffer();
+ }
+
+ if(result == DeviceException.MMSYSERR_NOERROR)
+ {
+ result = AddSysExBuffer();
+ }
+
+ if(result == DeviceException.MMSYSERR_NOERROR)
+ {
+ result = midiInStart(Handle);
+ }
+
+ if(result == MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ recording = true;
+ }
+ else
+ {
+ throw new InputDeviceException(result);
+ }
+ }
+ }
+
+ public void StopRecording()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("InputDevice");
+ }
+
+ #endregion
+
+ #region Guard
+
+ if(!recording)
+ {
+ return;
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ int result = midiInStop(Handle);
+
+ if(result == MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ recording = false;
+ }
+ else
+ {
+ throw new InputDeviceException(result);
+ }
+ }
+ }
+
+ public override void Reset()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("InputDevice");
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ resetting = true;
+
+ int result = midiInReset(Handle);
+
+ if(result == MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ recording = false;
+
+ while(bufferCount > 0)
+ {
+ Monitor.Wait(lockObject);
+ }
+
+ resetting = false;
+ }
+ else
+ {
+ resetting = false;
+
+ throw new InputDeviceException(result);
+ }
+ }
+ }
+
+ public static MidiInCaps GetDeviceCapabilities(int deviceID)
+ {
+ int result;
+ MidiInCaps caps = new MidiInCaps();
+
+ result = midiInGetDevCaps(deviceID, ref caps, SizeOfMidiHeader);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new InputDeviceException(result);
+ }
+
+ return caps;
+ }
+
+ public override void Dispose()
+ {
+ #region Guard
+
+ if(IsDisposed)
+ {
+ return;
+ }
+
+ #endregion
+
+ Dispose(true);
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Win32.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Win32.cs
new file mode 100644
index 0000000..a0846cb
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.Win32.cs
@@ -0,0 +1,95 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Sanford.Multimedia.Midi
+{
+ public partial class InputDevice
+ {
+ // Represents the method that handles messages from Windows.
+ private delegate void MidiInProc(int handle, int msg, int instance, int param1, int param2);
+
+ #region Win32 MIDI Input Functions and Constants
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInOpen(ref int handle, int deviceID,
+ MidiInProc proc, int instance, int flags);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInClose(int handle);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInStart(int handle);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInStop(int handle);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInReset(int handle);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInPrepareHeader(int handle,
+ IntPtr headerPtr, int sizeOfMidiHeader);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInUnprepareHeader(int handle,
+ IntPtr headerPtr, int sizeOfMidiHeader);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInAddBuffer(int handle,
+ IntPtr headerPtr, int sizeOfMidiHeader);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInGetDevCaps(int deviceID,
+ ref MidiInCaps caps, int sizeOfMidiInCaps);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInGetNumDevs();
+
+ private const int MIDI_IO_STATUS = 0x00000020;
+
+ private const int MIM_OPEN = 0x3C1;
+ private const int MIM_CLOSE = 0x3C2;
+ private const int MIM_DATA = 0x3C3;
+ private const int MIM_LONGDATA = 0x3C4;
+ private const int MIM_ERROR = 0x3C5;
+ private const int MIM_LONGERROR = 0x3C6;
+ private const int MIM_MOREDATA = 0x3CC;
+ private const int MHDR_DONE = 0x00000001;
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.cs
new file mode 100644
index 0000000..216b2d6
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/InputDevice.cs
@@ -0,0 +1,134 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Represents a MIDI device capable of receiving MIDI events.
+ ///
+ public partial class InputDevice : MidiDevice
+ {
+ protected override void Dispose(bool disposing)
+ {
+ if(disposing)
+ {
+ lock(lockObject)
+ {
+ Reset();
+
+ int result = midiInClose(handle);
+
+ if(result == MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ delegateQueue.Dispose();
+ }
+ else
+ {
+ throw new InputDeviceException(result);
+ }
+ }
+ }
+ else
+ {
+ midiInReset(Handle);
+ midiInClose(Handle);
+ }
+
+ base.Dispose(disposing);
+ }
+ }
+
+ ///
+ /// The exception that is thrown when a error occurs with the InputDevice
+ /// class.
+ ///
+ public class InputDeviceException : MidiDeviceException
+ {
+ #region InputDeviceException Members
+
+ #region Win32 Midi Input Error Function
+
+ [DllImport("winmm.dll")]
+ private static extern int midiInGetErrorText(int errCode,
+ StringBuilder errMsg, int sizeOfErrMsg);
+
+ #endregion
+
+ #region Fields
+
+ // Error message.
+ private StringBuilder errMsg = new StringBuilder(128);
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the InputDeviceException class with
+ /// the specified error code.
+ ///
+ ///
+ /// The error code.
+ ///
+ public InputDeviceException(int errCode) : base(errCode)
+ {
+ // Get error message.
+ midiInGetErrorText(errCode, errMsg, errMsg.Capacity);
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets a message that describes the current exception.
+ ///
+ public override string Message
+ {
+ get
+ {
+ return errMsg.ToString();
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/MidiInCaps.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/MidiInCaps.cs
new file mode 100644
index 0000000..d36da93
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/InputDevice Class/MidiInCaps.cs
@@ -0,0 +1,79 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Represents MIDI input device capabilities.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MidiInCaps
+ {
+ #region MidiInCaps Members
+
+ ///
+ /// Manufacturer identifier of the device driver for the Midi output
+ /// device.
+ ///
+ public short mid;
+
+ ///
+ /// Product identifier of the Midi output device.
+ ///
+ public short pid;
+
+ ///
+ /// Version number of the device driver for the Midi output device. The
+ /// high-order byte is the major version number, and the low-order byte
+ /// is the minor version number.
+ ///
+ public int driverVersion;
+
+ ///
+ /// Product name.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string name;
+
+ ///
+ /// Optional functionality supported by the device.
+ ///
+ public int support;
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiDevice.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiDevice.cs
new file mode 100644
index 0000000..b337739
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiDevice.cs
@@ -0,0 +1,120 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Sanford.Multimedia;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// The base class for all MIDI devices.
+ ///
+ public abstract class MidiDevice : Device
+ {
+ #region MidiDevice Members
+
+ #region Win32 Midi Device Functions
+
+ [DllImport("winmm.dll")]
+ private static extern int midiConnect(int handleA, int handleB, int reserved);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiDisconnect(int handleA, int handleB, int reserved);
+
+ #endregion
+
+ // Size of the MidiHeader structure.
+ protected static readonly int SizeOfMidiHeader;
+
+ static MidiDevice()
+ {
+ SizeOfMidiHeader = Marshal.SizeOf(typeof(MidiHeader));
+ }
+
+ public MidiDevice(int deviceID) : base(deviceID)
+ {
+ }
+
+ ///
+ /// Connects a MIDI InputDevice to a MIDI thru or OutputDevice, or
+ /// connects a MIDI thru device to a MIDI OutputDevice.
+ ///
+ ///
+ /// Handle to a MIDI InputDevice or a MIDI thru device (for thru
+ /// devices, this handle must belong to a MIDI OutputDevice).
+ ///
+ ///
+ /// Handle to the MIDI OutputDevice or thru device.
+ ///
+ ///
+ /// If an error occurred while connecting the two devices.
+ ///
+ public static void Connect(int handleA, int handleB)
+ {
+ int result = midiConnect(handleA, handleB, 0);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new MidiDeviceException(result);
+ }
+ }
+
+ ///
+ /// Disconnects a MIDI InputDevice from a MIDI thru or OutputDevice, or
+ /// disconnects a MIDI thru device from a MIDI OutputDevice.
+ ///
+ ///
+ /// Handle to a MIDI InputDevice or a MIDI thru device.
+ ///
+ ///
+ /// Handle to the MIDI OutputDevice to be disconnected.
+ ///
+ ///
+ /// If an error occurred while disconnecting the two devices.
+ ///
+ public static void Disconnect(int handleA, int handleB)
+ {
+ int result = midiDisconnect(handleA, handleB, 0);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new MidiDeviceException(result);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiDeviceException.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiDeviceException.cs
new file mode 100644
index 0000000..3b80ce2
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiDeviceException.cs
@@ -0,0 +1,73 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// The base class for all MIDI device exception classes.
+ ///
+ public class MidiDeviceException : DeviceException
+ {
+ #region Error Codes
+
+ public const int MIDIERR_UNPREPARED = 64; /* header not prepared */
+ public const int MIDIERR_STILLPLAYING = 65; /* still something playing */
+ public const int MIDIERR_NOMAP = 66; /* no configured instruments */
+ public const int MIDIERR_NOTREADY = 67; /* hardware is still busy */
+ public const int MIDIERR_NODEVICE = 68; /* port no longer connected */
+ public const int MIDIERR_INVALIDSETUP = 69; /* invalid MIF */
+ public const int MIDIERR_BADOPENMODE = 70; /* operation unsupported w/ open mode */
+ public const int MIDIERR_DONT_CONTINUE = 71; /* thru device 'eating' a message */
+ public const int MIDIERR_LASTERROR = 71; /* last error in range */
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the DeviceException class with the
+ /// specified error code.
+ ///
+ ///
+ /// The error code.
+ ///
+ public MidiDeviceException(int errCode) : base(errCode)
+ {
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiHeader.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiHeader.cs
new file mode 100644
index 0000000..7bc4ed6
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiHeader.cs
@@ -0,0 +1,101 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Represents the Windows Multimedia MIDIHDR structure.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct MidiHeader
+ {
+ #region MidiHeader Members
+
+ ///
+ /// Pointer to MIDI data.
+ ///
+ public IntPtr data;
+
+ ///
+ /// Size of the buffer.
+ ///
+ public int bufferLength;
+
+ ///
+ /// Actual amount of data in the buffer. This value should be less than
+ /// or equal to the value given in the dwBufferLength member.
+ ///
+ public int bytesRecorded;
+
+ ///
+ /// Custom user data.
+ ///
+ public int user;
+
+ ///
+ /// Flags giving information about the buffer.
+ ///
+ public int flags;
+
+ ///
+ /// Reserved; do not use.
+ ///
+ public IntPtr next;
+
+ ///
+ /// Reserved; do not use.
+ ///
+ public int reserved;
+
+ ///
+ /// Offset into the buffer when a callback is performed. (This
+ /// callback is generated because the MEVT_F_CALLBACK flag is
+ /// set in the dwEvent member of the MidiEventArgs structure.)
+ /// This offset enables an application to determine which
+ /// event caused the callback.
+ ///
+ public int offset;
+
+ ///
+ /// Reserved; do not use.
+ ///
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
+ public int[] reservedArray;
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiHeaderBuilder.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiHeaderBuilder.cs
new file mode 100644
index 0000000..f45c862
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/MidiHeaderBuilder.cs
@@ -0,0 +1,248 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections;
+using System.Runtime.InteropServices;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Builds a pointer to a MidiHeader structure.
+ ///
+ internal class MidiHeaderBuilder
+ {
+ // The length of the system exclusive buffer.
+ private int bufferLength;
+
+ // The system exclusive data.
+ private byte[] data;
+
+ // Indicates whether the pointer to the MidiHeader has been built.
+ private bool built = false;
+
+ // The built pointer to the MidiHeader.
+ private IntPtr result;
+
+ ///
+ /// Initializes a new instance of the MidiHeaderBuilder.
+ ///
+ public MidiHeaderBuilder()
+ {
+ BufferLength = 1;
+ }
+
+ #region Methods
+
+ ///
+ /// Builds the pointer to the MidiHeader structure.
+ ///
+ public void Build()
+ {
+ MidiHeader header = new MidiHeader();
+
+ // Initialize the MidiHeader.
+ header.bufferLength = BufferLength;
+ header.bytesRecorded = 0;
+ header.data = Marshal.AllocHGlobal(BufferLength);
+ header.flags = 0;
+
+ // Write data to the MidiHeader.
+ for(int i = 0; i < BufferLength; i++)
+ {
+ Marshal.WriteByte(header.data, i, data[i]);
+ }
+
+ try
+ {
+ result = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MidiHeader)));
+ }
+ catch(Exception)
+ {
+ Marshal.FreeHGlobal(header.data);
+
+ throw;
+ }
+
+ try
+ {
+ Marshal.StructureToPtr(header, result, false);
+ }
+ catch(Exception)
+ {
+ Marshal.FreeHGlobal(header.data);
+ Marshal.FreeHGlobal(result);
+
+ throw;
+ }
+
+ built = true;
+ }
+
+ ///
+ /// Initializes the MidiHeaderBuilder with the specified SysExMessage.
+ ///
+ ///
+ /// The SysExMessage to use for initializing the MidiHeaderBuilder.
+ ///
+ public void InitializeBuffer(SysExMessage message)
+ {
+ // If this is a start system exclusive message.
+ if(message.SysExType == SysExType.Start)
+ {
+ BufferLength = message.Length;
+
+ // Copy entire message.
+ for(int i = 0; i < BufferLength; i++)
+ {
+ data[i] = message[i];
+ }
+ }
+ // Else this is a continuation message.
+ else
+ {
+ BufferLength = message.Length - 1;
+
+ // Copy all but the first byte of message.
+ for(int i = 0; i < BufferLength; i++)
+ {
+ data[i] = message[i + 1];
+ }
+ }
+ }
+
+ public void InitializeBuffer(ICollection events)
+ {
+ #region Require
+
+ if(events == null)
+ {
+ throw new ArgumentNullException("events");
+ }
+ else if(events.Count % 4 != 0)
+ {
+ throw new ArgumentException("Stream events not word aligned.");
+ }
+
+ #endregion
+
+ #region Guard
+
+ if(events.Count == 0)
+ {
+ return;
+ }
+
+ #endregion
+
+ BufferLength = events.Count;
+
+ events.CopyTo(data, 0);
+ }
+
+ ///
+ /// Releases the resources associated with the built MidiHeader pointer.
+ ///
+ public void Destroy()
+ {
+ #region Require
+
+ if(!built)
+ {
+ throw new InvalidOperationException("Cannot destroy MidiHeader");
+ }
+
+ #endregion
+
+ Destroy(result);
+ }
+
+ ///
+ /// Releases the resources associated with the specified MidiHeader pointer.
+ ///
+ ///
+ /// The MidiHeader pointer.
+ ///
+ public void Destroy(IntPtr headerPtr)
+ {
+ MidiHeader header = (MidiHeader)Marshal.PtrToStructure(headerPtr, typeof(MidiHeader));
+
+ Marshal.FreeHGlobal(header.data);
+ Marshal.FreeHGlobal(headerPtr);
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// The length of the system exclusive buffer.
+ ///
+ public int BufferLength
+ {
+ get
+ {
+ return bufferLength;
+ }
+ set
+ {
+ #region Require
+
+ if(value <= 0)
+ {
+ throw new ArgumentOutOfRangeException("BufferLength", value,
+ "MIDI header buffer length out of range.");
+ }
+
+ #endregion
+
+ bufferLength = value;
+ data = new byte[value];
+ }
+ }
+
+ ///
+ /// Gets the pointer to the MidiHeader.
+ ///
+ public IntPtr Result
+ {
+ get
+ {
+ return result;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/MidiOutCaps.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/MidiOutCaps.cs
new file mode 100644
index 0000000..bfab960
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/MidiOutCaps.cs
@@ -0,0 +1,107 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Represents MIDI output device capabilities.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct MidiOutCaps
+ {
+ #region MidiOutCaps Members
+
+ ///
+ /// Manufacturer identifier of the device driver for the Midi output
+ /// device.
+ ///
+ public short mid;
+
+ ///
+ /// Product identifier of the Midi output device.
+ ///
+ public short pid;
+
+ ///
+ /// Version number of the device driver for the Midi output device. The
+ /// high-order byte is the major version number, and the low-order byte
+ /// is the minor version number.
+ ///
+ public int driverVersion;
+
+ ///
+ /// Product name.
+ ///
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
+ public string name;
+
+ ///
+ /// Flags describing the type of the Midi output device.
+ ///
+ public short technology;
+
+ ///
+ /// Number of voices supported by an internal synthesizer device. If
+ /// the device is a port, this member is not meaningful and is set
+ /// to 0.
+ ///
+ public short voices;
+
+ ///
+ /// Maximum number of simultaneous notes that can be played by an
+ /// internal synthesizer device. If the device is a port, this member
+ /// is not meaningful and is set to 0.
+ ///
+ public short notes;
+
+ ///
+ /// Channels that an internal synthesizer device responds to, where the
+ /// least significant bit refers to channel 0 and the most significant
+ /// bit to channel 15. Port devices that transmit on all channels set
+ /// this member to 0xFFFF.
+ ///
+ public short channelMask;
+
+ ///
+ /// Optional functionality supported by the device.
+ ///
+ public int support;
+
+ #endregion
+ }
+
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/NoOpEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/NoOpEventArgs.cs
new file mode 100644
index 0000000..ceed7af
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/NoOpEventArgs.cs
@@ -0,0 +1,59 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ ///
+ ///
+ public class NoOpEventArgs : EventArgs
+ {
+ private int data;
+
+ public NoOpEventArgs(int data)
+ {
+ this.data = data;
+ }
+
+ public int Data
+ {
+ get
+ {
+ return data;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputDevice.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputDevice.cs
new file mode 100644
index 0000000..6484cd3
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputDevice.cs
@@ -0,0 +1,297 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Represents a device capable of sending MIDI messages.
+ ///
+ public sealed class OutputDevice : OutputDeviceBase
+ {
+ #region Win32 Midi Output Functions and Constants
+
+ [DllImport("winmm.dll")]
+ private static extern int midiOutOpen(ref int handle, int deviceID,
+ MidiOutProc proc, int instance, int flags);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiOutClose(int handle);
+
+ #endregion
+
+ private MidiOutProc midiOutProc;
+
+ private bool runningStatusEnabled = false;
+
+ private int runningStatus = 0;
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the OutputDevice class.
+ ///
+ public OutputDevice(int deviceID) : base(deviceID)
+ {
+ midiOutProc = HandleMessage;
+
+ int result = midiOutOpen(ref hndle, deviceID, midiOutProc, 0, CALLBACK_FUNCTION);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+
+ #endregion
+
+ protected override void Dispose(bool disposing)
+ {
+ if(disposing)
+ {
+ lock(lockObject)
+ {
+ Reset();
+
+ // Close the OutputDevice.
+ int result = midiOutClose(Handle);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ // Throw an exception.
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+ else
+ {
+ midiOutReset(Handle);
+ midiOutClose(Handle);
+ }
+
+ base.Dispose(disposing);
+ }
+
+ ///
+ /// Closes the OutputDevice.
+ ///
+ ///
+ /// If an error occurred while closing the OutputDevice.
+ ///
+ public override void Close()
+ {
+ #region Guard
+
+ if(IsDisposed)
+ {
+ return;
+ }
+
+ #endregion
+
+ Dispose(true);
+ }
+
+ ///
+ /// Resets the OutputDevice.
+ ///
+ public override void Reset()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ #endregion
+
+ runningStatus = 0;
+
+ base.Reset();
+ }
+
+ public override void Send(ChannelMessage message)
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ // If running status is enabled.
+ if(runningStatusEnabled)
+ {
+ // If the message's status value matches the running status.
+ if(message.Status == runningStatus)
+ {
+ // Send only the two data bytes without the status byte.
+ Send(message.Message >> 8);
+ }
+ // Else the message's status value does not match the running
+ // status.
+ else
+ {
+ // Send complete message with status byte.
+ Send(message.Message);
+
+ // Update running status.
+ runningStatus = message.Status;
+ }
+ }
+ // Else running status has not been enabled.
+ else
+ {
+ Send(message.Message);
+ }
+ }
+ }
+
+ public override void Send(SysExMessage message)
+ {
+ // System exclusive cancels running status.
+ runningStatus = 0;
+
+ base.Send(message);
+ }
+
+ public override void Send(SysCommonMessage message)
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ #endregion
+
+ // System common cancels running status.
+ runningStatus = 0;
+
+ base.Send(message);
+ }
+
+ #region Properties
+
+ ///
+ /// Gets or sets a value indicating whether the OutputDevice uses
+ /// a running status.
+ ///
+ public bool RunningStatusEnabled
+ {
+ get
+ {
+ return runningStatusEnabled;
+ }
+ set
+ {
+ runningStatusEnabled = value;
+
+ // Reset running status.
+ runningStatus = 0;
+ }
+ }
+
+ #endregion
+ }
+
+ ///
+ /// The exception that is thrown when a error occurs with the OutputDevice
+ /// class.
+ ///
+ public class OutputDeviceException : MidiDeviceException
+ {
+ #region OutputDeviceException Members
+
+ #region Win32 Midi Output Error Function
+
+ [DllImport("winmm.dll")]
+ private static extern int midiOutGetErrorText(int errCode,
+ StringBuilder message, int sizeOfMessage);
+
+ #endregion
+
+ #region Fields
+
+ // The error message.
+ private StringBuilder message = new StringBuilder(128);
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the OutputDeviceException class with
+ /// the specified error code.
+ ///
+ ///
+ /// The error code.
+ ///
+ public OutputDeviceException(int errCode) : base(errCode)
+ {
+ // Get error message.
+ midiOutGetErrorText(errCode, message, message.Capacity);
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets a message that describes the current exception.
+ ///
+ public override string Message
+ {
+ get
+ {
+ return message.ToString();
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputDeviceBase.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputDeviceBase.cs
new file mode 100644
index 0000000..d29d2b7
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputDeviceBase.cs
@@ -0,0 +1,342 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Sanford.Threading;
+
+namespace Sanford.Multimedia.Midi
+{
+ public abstract class OutputDeviceBase : MidiDevice
+ {
+ [DllImport("winmm.dll")]
+ protected static extern int midiOutReset(int handle);
+
+ [DllImport("winmm.dll")]
+ protected static extern int midiOutShortMsg(int handle, int message);
+
+ [DllImport("winmm.dll")]
+ protected static extern int midiOutPrepareHeader(int handle,
+ IntPtr headerPtr, int sizeOfMidiHeader);
+
+ [DllImport("winmm.dll")]
+ protected static extern int midiOutUnprepareHeader(int handle,
+ IntPtr headerPtr, int sizeOfMidiHeader);
+
+ [DllImport("winmm.dll")]
+ protected static extern int midiOutLongMsg(int handle,
+ IntPtr headerPtr, int sizeOfMidiHeader);
+
+ [DllImport("winmm.dll")]
+ protected static extern int midiOutGetDevCaps(int deviceID,
+ ref MidiOutCaps caps, int sizeOfMidiOutCaps);
+
+ [DllImport("winmm.dll")]
+ protected static extern int midiOutGetNumDevs();
+
+ protected const int MOM_OPEN = 0x3C7;
+ protected const int MOM_CLOSE = 0x3C8;
+ protected const int MOM_DONE = 0x3C9;
+
+ protected delegate void GenericDelegate(T args);
+
+ // Represents the method that handles messages from Windows.
+ protected delegate void MidiOutProc(int handle, int msg, int instance, int param1, int param2);
+
+ // For releasing buffers.
+ protected DelegateQueue delegateQueue = new DelegateQueue();
+
+ protected readonly object lockObject = new object();
+
+ // The number of buffers still in the queue.
+ protected int bufferCount = 0;
+
+ // Builds MidiHeader structures for sending system exclusive messages.
+ private MidiHeaderBuilder headerBuilder = new MidiHeaderBuilder();
+
+ // The device handle.
+ protected int hndle = 0;
+
+ public OutputDeviceBase(int deviceID) : base(deviceID)
+ {
+ }
+
+ ~OutputDeviceBase()
+ {
+ Dispose(false);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if(disposing)
+ {
+ delegateQueue.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ public virtual void Send(ChannelMessage message)
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ #endregion
+
+ Send(message.Message);
+ }
+
+ public virtual void Send(SysExMessage message)
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ headerBuilder.InitializeBuffer(message);
+ headerBuilder.Build();
+
+ // Prepare system exclusive buffer.
+ int result = midiOutPrepareHeader(Handle, headerBuilder.Result, SizeOfMidiHeader);
+
+ // If the system exclusive buffer was prepared successfully.
+ if(result == MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ bufferCount++;
+
+ // Send system exclusive message.
+ result = midiOutLongMsg(Handle, headerBuilder.Result, SizeOfMidiHeader);
+
+ // If the system exclusive message could not be sent.
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ midiOutUnprepareHeader(Handle, headerBuilder.Result, SizeOfMidiHeader);
+ bufferCount--;
+ headerBuilder.Destroy();
+
+ // Throw an exception.
+ throw new OutputDeviceException(result);
+ }
+ }
+ // Else the system exclusive buffer could not be prepared.
+ else
+ {
+ // Destroy system exclusive buffer.
+ headerBuilder.Destroy();
+
+ // Throw an exception.
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+
+ public virtual void Send(SysCommonMessage message)
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ #endregion
+
+ Send(message.Message);
+ }
+
+ public virtual void Send(SysRealtimeMessage message)
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ #endregion
+
+ Send(message.Message);
+ }
+
+ public override void Reset()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException(this.GetType().Name);
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ // Reset the OutputDevice.
+ int result = midiOutReset(Handle);
+
+ if(result == MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ while(bufferCount > 0)
+ {
+ Monitor.Wait(lockObject);
+ }
+ }
+ else
+ {
+ // Throw an exception.
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+
+ protected void Send(int message)
+ {
+ lock(lockObject)
+ {
+ int result = midiOutShortMsg(Handle, message);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+
+ public static MidiOutCaps GetDeviceCapabilities(int deviceID)
+ {
+ MidiOutCaps caps = new MidiOutCaps();
+
+ // Get the device's capabilities.
+ int result = midiOutGetDevCaps(deviceID, ref caps, Marshal.SizeOf(caps));
+
+ // If the capabilities could not be retrieved.
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ // Throw an exception.
+ throw new OutputDeviceException(result);
+ }
+
+ return caps;
+ }
+
+ // Handles Windows messages.
+ protected virtual void HandleMessage(int handle, int msg, int instance, int param1, int param2)
+ {
+ if(msg == MOM_OPEN)
+ {
+ }
+ else if(msg == MOM_CLOSE)
+ {
+ }
+ else if(msg == MOM_DONE)
+ {
+ delegateQueue.Post(ReleaseBuffer, new IntPtr(param1));
+ }
+ }
+
+ // Releases buffers.
+ private void ReleaseBuffer(object state)
+ {
+ lock(lockObject)
+ {
+ IntPtr headerPtr = (IntPtr)state;
+
+ // Unprepare the buffer.
+ int result = midiOutUnprepareHeader(Handle, headerPtr, SizeOfMidiHeader);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ Exception ex = new OutputDeviceException(result);
+
+ OnError(new ErrorEventArgs(ex));
+ }
+
+ // Release the buffer resources.
+ headerBuilder.Destroy(headerPtr);
+
+ bufferCount--;
+
+ Monitor.Pulse(lockObject);
+
+ Debug.Assert(bufferCount >= 0);
+ }
+ }
+
+ public override void Dispose()
+ {
+ #region Guard
+
+ if(IsDisposed)
+ {
+ return;
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ Close();
+ }
+ }
+
+ public override int Handle
+ {
+ get
+ {
+ return hndle;
+ }
+ }
+
+ public static int DeviceCount
+ {
+ get
+ {
+ return midiOutGetNumDevs();
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputStream.cs b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputStream.cs
new file mode 100644
index 0000000..a0e8e15
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Device Classes/OutputDevice Classes/OutputStream.cs
@@ -0,0 +1,616 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Sanford.Multimedia.Timers;
+
+namespace Sanford.Multimedia.Midi
+{
+ public sealed class OutputStream : OutputDeviceBase
+ {
+ [DllImport("winmm.dll")]
+ private static extern int midiStreamOpen(ref int handle, ref int deviceID, int reserved,
+ OutputDevice.MidiOutProc proc, int instance, uint flag);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiStreamClose(int handle);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiStreamOut(int handle, IntPtr headerPtr, int sizeOfMidiHeader);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiStreamPause(int handle);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiStreamPosition(int handle, ref Time t, int sizeOfTime);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiStreamProperty(int handle, ref Property p, uint flags);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiStreamRestart(int handle);
+
+ [DllImport("winmm.dll")]
+ private static extern int midiStreamStop(int handle);
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct Property
+ {
+ public int sizeOfProperty;
+ public int property;
+ }
+
+ private const uint MIDIPROP_SET = 0x80000000;
+ private const uint MIDIPROP_GET = 0x40000000;
+ private const uint MIDIPROP_TIMEDIV = 0x00000001;
+ private const uint MIDIPROP_TEMPO = 0x00000002;
+
+ private const byte MEVT_CALLBACK = 0x40;
+
+ private const byte MEVT_SHORTMSG = 0x00;
+ private const byte MEVT_TEMPO = 0x01;
+ private const byte MEVT_NOP = 0x02;
+ private const byte MEVT_LONGMSG = 0x80;
+ private const byte MEVT_COMMENT = 0x82;
+ private const byte MEVT_VERSION = 0x84;
+
+ private const int MOM_POSITIONCB = 0x3CA;
+
+ private const int SizeOfMidiEvent = 12;
+
+ private const int EventTypeIndex = 11;
+
+ private const int EventCodeOffset = 8;
+
+ private MidiOutProc midiOutProc;
+
+ private int offsetTicks = 0;
+
+ private byte[] streamID = new byte[4];
+
+ private List events = new List();
+
+ private MidiHeaderBuilder headerBuilder = new MidiHeaderBuilder();
+
+ public event EventHandler NoOpOccurred;
+
+ public OutputStream(int deviceID) : base(deviceID)
+ {
+ midiOutProc = HandleMessage;
+
+ int result = midiStreamOpen(ref hndle, ref deviceID, 1, midiOutProc, 0, CALLBACK_FUNCTION);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if(disposing)
+ {
+ lock(lockObject)
+ {
+ Reset();
+
+ int result = midiStreamClose(Handle);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+ else
+ {
+ midiOutReset(Handle);
+ midiStreamClose(Handle);
+ }
+
+ base.Dispose(disposing);
+ }
+
+ public override void Close()
+ {
+ #region Guard
+
+ if(!IsDisposed)
+ {
+ return;
+ }
+
+ #endregion
+
+ Dispose(true);
+ }
+
+ public void StartPlaying()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ int result = midiStreamRestart(Handle);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+
+ public void PausePlaying()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ int result = midiStreamPause(Handle);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+
+ public void StopPlaying()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ int result = midiStreamStop(Handle);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+
+ public override void Reset()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ offsetTicks = 0;
+ events.Clear();
+
+ base.Reset();
+ }
+
+ public void Write(MidiEvent e)
+ {
+ switch(e.MidiMessage.MessageType)
+ {
+ case MessageType.Channel:
+ case MessageType.SystemCommon:
+ case MessageType.SystemRealtime:
+ Write(e.DeltaTicks, (ShortMessage)e.MidiMessage);
+ break;
+
+ case MessageType.SystemExclusive:
+ Write(e.DeltaTicks, (SysExMessage)e.MidiMessage);
+ break;
+
+ case MessageType.Meta:
+ Write(e.DeltaTicks, (MetaMessage)e.MidiMessage);
+ break;
+ }
+ }
+
+ private void Write(int deltaTicks, ShortMessage message)
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ // Delta time.
+ events.AddRange(BitConverter.GetBytes(deltaTicks + offsetTicks));
+
+ // Stream ID.
+ events.AddRange(streamID);
+
+ // Event code.
+ byte[] eventCode = message.GetBytes();
+ eventCode[eventCode.Length - 1] = MEVT_SHORTMSG;
+ events.AddRange(eventCode);
+
+ offsetTicks = 0;
+ }
+
+ private void Write(int deltaTicks, SysExMessage message)
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ // Delta time.
+ events.AddRange(BitConverter.GetBytes(deltaTicks + offsetTicks));
+
+ // Stream ID.
+ events.AddRange(streamID);
+
+ // Event code.
+ byte[] eventCode = BitConverter.GetBytes(message.Length);
+ eventCode[eventCode.Length - 1] = MEVT_LONGMSG;
+ events.AddRange(eventCode);
+
+ byte[] sysExData;
+
+ if(message.Length % 4 != 0)
+ {
+ sysExData = new byte[message.Length + (message.Length % 4)];
+ message.GetBytes().CopyTo(sysExData, 0);
+ }
+ else
+ {
+ sysExData = message.GetBytes();
+ }
+
+ // SysEx data.
+ events.AddRange(sysExData);
+
+ offsetTicks = 0;
+ }
+
+ private void Write(int deltaTicks, MetaMessage message)
+ {
+ if(message.MetaType == MetaType.Tempo)
+ {
+ // Delta time.
+ events.AddRange(BitConverter.GetBytes(deltaTicks + offsetTicks));
+
+ // Stream ID.
+ events.AddRange(streamID);
+
+ TempoChangeBuilder builder = new TempoChangeBuilder(message);
+
+ byte[] t = BitConverter.GetBytes(builder.Tempo);
+
+ t[t.Length - 1] = MEVT_SHORTMSG | MEVT_TEMPO;
+
+ // Event code.
+ events.AddRange(t);
+
+ offsetTicks = 0;
+ }
+ else
+ {
+ offsetTicks += deltaTicks;
+ }
+ }
+
+ public void WriteNoOp(int deltaTicks, int data)
+ {
+ // Delta time.
+ events.AddRange(BitConverter.GetBytes(deltaTicks + offsetTicks));
+
+ // Stream ID.
+ events.AddRange(streamID);
+
+ // Event code.
+ byte[] eventCode = BitConverter.GetBytes(data);
+ eventCode[eventCode.Length - 1] = (byte)(MEVT_NOP | MEVT_CALLBACK);
+ events.AddRange(eventCode);
+
+ offsetTicks = 0;
+ }
+
+ public void Flush()
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ lock(lockObject)
+ {
+ headerBuilder.InitializeBuffer(events);
+ headerBuilder.Build();
+
+ events.Clear();
+
+ int result = midiOutPrepareHeader(Handle, headerBuilder.Result, SizeOfMidiHeader);
+
+ if(result == MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ bufferCount++;
+ }
+ else
+ {
+ headerBuilder.Destroy();
+
+ throw new OutputDeviceException(result);
+ }
+
+ result = midiStreamOut(Handle, headerBuilder.Result, SizeOfMidiHeader);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ midiOutUnprepareHeader(Handle, headerBuilder.Result, SizeOfMidiHeader);
+
+ headerBuilder.Destroy();
+
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+
+ public Time GetTime(TimeType type)
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ Time t = new Time();
+
+ t.type = (int)type;
+
+ lock(lockObject)
+ {
+ int result = midiStreamPosition(Handle, ref t, Marshal.SizeOf(typeof(Time)));
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+
+ return t;
+ }
+
+ private void OnNoOpOccurred(NoOpEventArgs e)
+ {
+ EventHandler handler = NoOpOccurred;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ protected override void HandleMessage(int handle, int msg, int instance, int param1, int param2)
+ {
+ if(msg == MOM_POSITIONCB)
+ {
+ delegateQueue.Post(HandleNoOp, new IntPtr(param1));
+ }
+ else
+ {
+ base.HandleMessage(handle, msg, instance, param1, param2);
+ }
+ }
+
+ private void HandleNoOp(object state)
+ {
+ IntPtr headerPtr = (IntPtr)state;
+ MidiHeader header = (MidiHeader)Marshal.PtrToStructure(headerPtr, typeof(MidiHeader));
+
+ byte[] midiEvent = new byte[SizeOfMidiEvent];
+
+ for(int i = 0; i < midiEvent.Length; i++)
+ {
+ midiEvent[i] = Marshal.ReadByte(header.data, header.offset + i);
+ }
+
+ // If this is a NoOp event.
+ if((midiEvent[EventTypeIndex] & MEVT_NOP) == MEVT_NOP)
+ {
+ // Clear the event type byte.
+ midiEvent[EventTypeIndex] = 0;
+
+ NoOpEventArgs e = new NoOpEventArgs(BitConverter.ToInt32(midiEvent, EventCodeOffset));
+
+ context.Post(new SendOrPostCallback(delegate(object s)
+ {
+ OnNoOpOccurred(e);
+ }), null);
+ }
+ }
+
+ public int Division
+ {
+ get
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ Property d = new Property();
+
+ d.sizeOfProperty = Marshal.SizeOf(typeof(Property));
+
+ lock(lockObject)
+ {
+ int result = midiStreamProperty(Handle, ref d, MIDIPROP_GET | MIDIPROP_TIMEDIV);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+
+ return d.property;
+ }
+ set
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+ else if((value % PpqnClock.PpqnMinValue) != 0)
+ {
+ throw new ArgumentException();
+ }
+
+ #endregion
+
+ Property d = new Property();
+
+ d.sizeOfProperty = Marshal.SizeOf(typeof(Property));
+ d.property = value;
+
+ lock(lockObject)
+ {
+ int result = midiStreamProperty(Handle, ref d, MIDIPROP_SET | MIDIPROP_TIMEDIV);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+ }
+
+ public int Tempo
+ {
+ get
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+
+ #endregion
+
+ Property t = new Property();
+ t.sizeOfProperty = Marshal.SizeOf(typeof(Property));
+
+ lock(lockObject)
+ {
+ int result = midiStreamProperty(Handle, ref t, MIDIPROP_GET | MIDIPROP_TEMPO);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+
+ return t.property;
+ }
+ set
+ {
+ #region Require
+
+ if(IsDisposed)
+ {
+ throw new ObjectDisposedException("OutputStream");
+ }
+ else if(value < 0)
+ {
+ throw new ArgumentOutOfRangeException("Tempo", value,
+ "Tempo out of range.");
+ }
+
+ #endregion
+
+ Property t = new Property();
+ t.sizeOfProperty = Marshal.SizeOf(typeof(Property));
+ t.property = value;
+
+ lock(lockObject)
+ {
+ int result = midiStreamProperty(Handle, ref t, MIDIPROP_SET | MIDIPROP_TEMPO);
+
+ if(result != MidiDeviceException.MMSYSERR_NOERROR)
+ {
+ throw new OutputDeviceException(result);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Documentation.xml b/DependenciesCode/Sanford.Multimedia.Midi/Documentation.xml
new file mode 100644
index 0000000..68eb04f
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Documentation.xml
@@ -0,0 +1,2455 @@
+
+
+
+ Multimedia.Midi
+
+
+
+
+ Represents the base class for all MIDI devices.
+
+
+
+
+ Connects a MIDI InputDevice to a MIDI thru or OutputDevice, or
+ connects a MIDI thru device to a MIDI OutputDevice.
+
+
+ Handle to a MIDI InputDevice or a MIDI thru device (for thru
+ devices, this handle must belong to a MIDI OutputDevice).
+
+
+ Handle to the MIDI OutputDevice or thru device.
+
+
+ If an error occurred while connecting the two devices.
+
+
+
+
+ Disconnects a MIDI InputDevice from a MIDI thru or OutputDevice, or
+ disconnects a MIDI thru device from a MIDI OutputDevice.
+
+
+ Handle to a MIDI InputDevice or a MIDI thru device.
+
+
+ Handle to the MIDI OutputDevice to be disconnected.
+
+
+ If an error occurred while disconnecting the two devices.
+
+
+
+
+ Opens the MIDI device.
+
+
+ The device ID.
+
+
+
+
+ Closes the MIDI device.
+
+
+
+
+ Resets the MIDI device.
+
+
+
+
+ Gets the device handle.
+
+
+
+
+ Gets a value indicating whether the device is open.
+
+
+
+
+ Summary description for DeviceException.
+
+
+
+
+ Gets the error code that raised the exception.
+
+
+
+
+ Represents a MIDI device capable of receiving MIDI messages.
+
+
+
+
+ Represents a source of ChannelMessages.
+
+
+
+
+ Occurs when a ChannelMessage is received, generated, or
+ encountered by a IChannelSource.
+
+
+
+
+ Summary description for ISysExSource.
+
+
+
+
+ Summary description for ISysCommonSource.
+
+
+
+
+ Summary description for ISysRealtimeSource.
+
+
+
+
+ The exception that is thrown when a error occurs with the InputDevice
+ class.
+
+
+
+
+ Initializes a new instance of the InputDeviceException class with
+ the specified error code.
+
+
+ The error code.
+
+
+
+
+ Gets a message that describes the current exception.
+
+
+
+
+ Represents data for the InvalidShortMessageEventArgs class.
+
+
+
+
+ Initializes a new instance of the InvalidShortMessageEventArgs class
+ with the specified invalid short message.
+
+
+ The invalid short message as a packed integer.
+
+
+
+
+ Gets the invalid short message as a packed integer.
+
+
+
+
+ Represents data for the InvalidSysExMessageOccurred event.
+
+
+
+
+ Initializes a new instance of the InvalidSysExMessageEventArgs class
+ with the specified invalid system exclusive data.
+
+
+ The invalid system exclusive data.
+
+
+
+
+ Gets the element at the specified index.
+
+
+
+
+ Gets the length of the invalid system exclusive data.
+
+
+
+
+ Represents the Windows Multimedia MIDIHDR structure.
+
+
+
+
+ Builds a pointer to a MidiHeader structure.
+
+
+
+
+ Releases the resources associated with the built MidiHeader pointer.
+
+
+
+
+ Releases the resources associated with the specified MidiHeader pointer.
+
+
+ The MidiHeader pointer.
+
+
+
+
+ Represents Midi input device capabilities.
+
+
+
+
+ Manufacturer identifier of the device driver for the Midi output
+ device.
+
+
+
+
+ Product identifier of the Midi output device.
+
+
+
+
+ Version number of the device driver for the Midi output device. The
+ high-order byte is the major version number, and the low-order byte
+ is the minor version number.
+
+
+
+
+ Product name.
+
+
+
+
+ Optional functionality supported by the device.
+
+
+
+
+ Represents Midi output device capabilities.
+
+
+
+
+ Manufacturer identifier of the device driver for the Midi output
+ device.
+
+
+
+
+ Product identifier of the Midi output device.
+
+
+
+
+ Version number of the device driver for the Midi output device. The
+ high-order byte is the major version number, and the low-order byte
+ is the minor version number.
+
+
+
+
+ Product name.
+
+
+
+
+ Flags describing the type of the Midi output device.
+
+
+
+
+ Number of voices supported by an internal synthesizer device. If
+ the device is a port, this member is not meaningful and is set
+ to 0.
+
+
+
+
+ Maximum number of simultaneous notes that can be played by an
+ internal synthesizer device. If the device is a port, this member
+ is not meaningful and is set to 0.
+
+
+
+
+ Channels that an internal synthesizer device responds to, where the
+ least significant bit refers to channel 0 and the most significant
+ bit to channel 15. Port devices that transmit on all channels set
+ this member to 0xFFFF.
+
+
+
+
+ Optional functionality supported by the device.
+
+
+
+
+ Represents a device capable of sending MIDI messages.
+
+
+
+
+ Represents functionality for connecting to and disconnecting from an
+ IChannelSource.
+
+
+
+
+ Connects the IChannelSink to the specified IChannelSource.
+
+
+ The IChannelSource to which to connect.
+
+
+
+
+ Disconnects the IChannelSink from the specified IChannelSource.
+
+
+ The IChannelSource from which to disconnect.
+
+
+
+
+ Summary description for ISysExSink.
+
+
+
+
+ Summary description for ISysCommonSink.
+
+
+
+
+ Summary description for ISysRealtimeMessage.
+
+
+
+
+ Disposes of the OutputDevice.
+
+
+
+
+ Occurs when the OutputDevice has been opened.
+
+
+
+
+ Occurs when the OutputDevice has been closed.
+
+
+
+
+ Occurs when the OutputDevice has been disposed.
+
+
+
+
+ The exception that is thrown when a error occurs with the OutputDevice
+ class.
+
+
+
+
+ Initializes a new instance of the OutputDeviceException class with
+ the specified error code.
+
+
+ The error code.
+
+
+
+
+ Gets a message that describes the current exception.
+
+
+
+
+ Provides functionality for building ChannelMessages.
+
+
+
+
+ Represents functionality for building MIDI messages.
+
+
+
+
+ Builds the MIDI message.
+
+
+
+
+ Initializes a new instance of the ChannelMessageBuilder class.
+
+
+
+
+ Initializes a new instance of the ChannelMessageBuilder class with
+ the specified ChannelMessage.
+
+
+ The ChannelMessage to use for initializing the ChannelMessageBuilder.
+
+
+ The ChannelMessageBuilder uses the specified ChannelMessage to
+ initialize its property values.
+
+
+
+
+ Initializes the ChannelMessageBuilder with the specified
+ ChannelMessage.
+
+
+ The ChannelMessage to use for initializing the ChannelMessageBuilder.
+
+
+
+
+ Clears the ChannelMessage cache.
+
+
+
+
+ Builds a ChannelMessage.
+
+
+
+
+ Gets the number of messages in the ChannelMessage cache.
+
+
+
+
+ Gets the built ChannelMessage.
+
+
+
+
+ Gets or sets the ChannelMessage as a packed integer.
+
+
+
+
+ Gets or sets the Command value to use for building the
+ ChannelMessage.
+
+
+
+
+ Gets or sets the MIDI channel to use for building the
+ ChannelMessage.
+
+
+ MidiChannel is set to a value less than zero or greater than 15.
+
+
+
+
+ Gets or sets the first data value to use for building the
+ ChannelMessage.
+
+
+ Data1 is set to a value less than zero or greater than 127.
+
+
+
+
+ Gets or sets the second data value to use for building the
+ ChannelMessage.
+
+
+ Data2 is set to a value less than zero or greater than 127.
+
+
+
+
+ Provides functionality for building meta text messages.
+
+
+
+
+ Initializes a new instance of the MetaMessageTextBuilder class.
+
+
+
+
+ Initializes a new instance of the MetaMessageTextBuilder class with the
+ specified type.
+
+
+ The type of MetaMessage.
+
+
+ If the MetaMessage type is not a text based type.
+
+
+ The MetaMessage type must be one of the following text based
+ types:
+
+ -
+ Copyright
+
+ -
+ Cuepoint
+
+ -
+ DeviceName
+
+ -
+ InstrumentName
+
+ -
+ Lyric
+
+ -
+ Marker
+
+ -
+ ProgramName
+
+ -
+ Text
+
+ -
+ TrackName
+
+
+ If the MetaMessage is not a text based type, an exception
+ will be thrown.
+
+
+
+
+ Initializes a new instance of the MetaMessageTextBuilder class with the
+ specified type.
+
+
+ The type of MetaMessage.
+
+
+ If the MetaMessage type is not a text based type.
+
+
+ The MetaMessage type must be one of the following text based
+ types:
+
+ -
+ Copyright
+
+ -
+ Cuepoint
+
+ -
+ DeviceName
+
+ -
+ InstrumentName
+
+ -
+ Lyric
+
+ -
+ Marker
+
+ -
+ ProgramName
+
+ -
+ Text
+
+ -
+ TrackName
+
+
+ If the MetaMessage is not a text based type, an exception
+ will be thrown.
+
+
+
+
+ Initializes a new instance of the MetaMessageTextBuilder class with the
+ specified MetaMessage.
+
+
+ The MetaMessage to use for initializing the MetaMessageTextBuilder.
+
+
+ If the MetaMessage is not a text based type.
+
+
+ The MetaMessage must be one of the following text based types:
+
+ -
+ Copyright
+
+ -
+ Cuepoint
+
+ -
+ DeviceName
+
+ -
+ InstrumentName
+
+ -
+ Lyric
+
+ -
+ Marker
+
+ -
+ ProgramName
+
+ -
+ Text
+
+ -
+ TrackName
+
+
+ If the MetaMessage is not a text based type, an exception will be
+ thrown.
+
+
+
+
+ Initializes the MetaMessageTextBuilder with the specified MetaMessage.
+
+
+ The MetaMessage to use for initializing the MetaMessageTextBuilder.
+
+
+ If the MetaMessage is not a text based type.
+
+
+
+
+ Indicates whether or not the specified MetaType is a text based
+ type.
+
+
+ The MetaType to test.
+
+
+ true if the MetaType is a text based type;
+ otherwise, false.
+
+
+
+
+ Builds the text MetaMessage.
+
+
+
+
+ Gets or sets the text for the MetaMessage.
+
+
+
+
+ Gets or sets the MetaMessage type.
+
+
+ If the type is not a text based type.
+
+
+
+
+ Gets the built MetaMessage.
+
+
+
+
+ Provides functionality for building song position pointer messages.
+
+
+
+
+ Initializes a new instance of the SongPositionPointerBuilder class.
+
+
+
+
+ Initializes a new instance of the SongPositionPointerBuilder class
+ with the specified song position pointer message.
+
+
+ The song position pointer message to use for initializing the
+ SongPositionPointerBuilder.
+
+
+ If message is not a song position pointer message.
+
+
+
+
+ Initializes the SongPositionPointerBuilder with the specified
+ SysCommonMessage.
+
+
+ The SysCommonMessage to use to initialize the
+ SongPositionPointerBuilder.
+
+
+ If the SysCommonMessage is not a song position pointer message.
+
+
+
+
+ Builds a song position pointer message.
+
+
+
+
+ Gets or sets the sequence position in ticks.
+
+
+ Value is set to less than zero.
+
+
+ Note: the position in ticks value is converted to the song position
+ pointer value. Since the song position pointer has a lower
+ resolution than the position in ticks, there is a probable loss of
+ resolution when setting the position in ticks value.
+
+
+
+
+ Gets or sets the PulsesPerQuarterNote object.
+
+
+ Value is not a multiple of 24.
+
+
+
+
+ Gets or sets the song position.
+
+
+ Value is set to less than zero.
+
+
+
+
+ Gets the built song position pointer message.
+
+
+
+
+ Provides functionality for building SysCommonMessages.
+
+
+
+
+ Initializes a new instance of the SysCommonMessageBuilder class.
+
+
+
+
+ Initializes a new instance of the SysCommonMessageBuilder class
+ with the specified SystemCommonMessage.
+
+
+ The SysCommonMessage to use for initializing the
+ SysCommonMessageBuilder.
+
+
+ The SysCommonMessageBuilder uses the specified SysCommonMessage to
+ initialize its property values.
+
+
+
+
+ Initializes the SysCommonMessageBuilder with the specified
+ SysCommonMessage.
+
+
+ The SysCommonMessage to use for initializing the
+ SysCommonMessageBuilder.
+
+
+
+
+ Clears the SysCommonMessageBuilder cache.
+
+
+
+
+ Builds a SysCommonMessage.
+
+
+
+
+ Gets the number of messages in the SysCommonMessageBuilder cache.
+
+
+
+
+ Gets the built SysCommonMessage.
+
+
+
+
+ Gets or sets the SysCommonMessage as a packed integer.
+
+
+
+
+ Gets or sets the type of SysCommonMessage.
+
+
+
+
+ Gets or sets the first data value to use for building the
+ SysCommonMessage.
+
+
+ Data1 is set to a value less than zero or greater than 127.
+
+
+
+
+ Gets or sets the second data value to use for building the
+ SysCommonMessage.
+
+
+ Data2 is set to a value less than zero or greater than 127.
+
+
+
+
+ Provides functionality for building tempo messages.
+
+
+
+
+ Initializes a new instance of the TempoChangeBuilder class.
+
+
+
+
+ Initialize a new instance of the TempoChangeBuilder class with the
+ specified MetaMessage.
+
+
+ The MetaMessage to use for initializing the TempoChangeBuilder class.
+
+
+ If the specified MetaMessage is not a tempo type.
+
+
+ The TempoChangeBuilder uses the specified MetaMessage to initialize
+ its property values.
+
+
+
+
+ Initializes the TempoChangeBuilder with the specified MetaMessage.
+
+
+ The MetaMessage to use for initializing the TempoChangeBuilder.
+
+
+ If the specified MetaMessage is not a tempo type.
+
+
+
+
+ Builds the tempo change MetaMessage.
+
+
+
+
+ Gets or sets the Tempo object.
+
+
+ Value is set to less than zero.
+
+
+
+
+ Gets the built message.
+
+
+
+
+ Provides easy to use functionality for meta time signature messages.
+
+
+
+
+ Initializes a new instance of the TimeSignatureBuilder class.
+
+
+
+
+ Initializes a new instance of the TimeSignatureBuilder class with the
+ specified MetaMessage.
+
+
+ The MetaMessage to use for initializing the TimeSignatureBuilder class.
+
+
+ If the specified MetaMessage is not a time signature type.
+
+
+ The TimeSignatureBuilder uses the specified MetaMessage to
+ initialize its property values.
+
+
+
+
+ Initializes the TimeSignatureBuilder with the specified MetaMessage.
+
+
+ The MetaMessage to use for initializing the TimeSignatureBuilder.
+
+
+ If the specified MetaMessage is not a time signature type.
+
+
+
+
+ Builds the time signature message.
+
+
+
+
+ Gets or sets the numerator.
+
+
+ Numerator is set to a value less than one.
+
+
+
+
+ Gets or sets the denominator.
+
+
+ Denominator is set to a value less than 2.
+
+
+ Denominator is set to a value that is not a multiple of 2.
+
+
+
+
+ Gets or sets the clocks per metronome click.
+
+
+ Clocks per metronome click determines how many MIDI clocks occur
+ for each metronome click.
+
+
+
+
+ Gets or sets how many thirty second notes there are for each
+ quarter note.
+
+
+
+
+ Gets the built message.
+
+
+
+
+ Represents the method that handles ChannelMessage events.
+
+
+
+
+ Defines constants for ChannelMessage types.
+
+
+
+
+ Represents the note-off command type.
+
+
+
+
+ Represents the note-on command type.
+
+
+
+
+ Represents the poly pressure (aftertouch) command type.
+
+
+
+
+ Represents the controller command type.
+
+
+
+
+ Represents the program change command type.
+
+
+
+
+ Represents the channel pressure (aftertouch) command
+ type.
+
+
+
+
+ Represents the pitch wheel command type.
+
+
+
+
+ Defines constants for controller types.
+
+
+
+
+ The Bank Select coarse.
+
+
+
+
+ The Modulation Wheel coarse.
+
+
+
+
+ The Breath Control coarse.
+
+
+
+
+ The Foot Pedal coarse.
+
+
+
+
+ The Portamento Time coarse.
+
+
+
+
+ The Data Entry Slider coarse.
+
+
+
+
+ The Volume coarse.
+
+
+
+
+ The Balance coarse.
+
+
+
+
+ The Pan position coarse.
+
+
+
+
+ The Expression coarse.
+
+
+
+
+ The Effect Control 1 coarse.
+
+
+
+
+ The Effect Control 2 coarse.
+
+
+
+
+ The General Puprose Slider 1
+
+
+
+
+ The General Puprose Slider 2
+
+
+
+
+ The General Puprose Slider 3
+
+
+
+
+ The General Puprose Slider 4
+
+
+
+
+ The Bank Select fine.
+
+
+
+
+ The Modulation Wheel fine.
+
+
+
+
+ The Breath Control fine.
+
+
+
+
+ The Foot Pedal fine.
+
+
+
+
+ The Portamento Time fine.
+
+
+
+
+ The Data Entry Slider fine.
+
+
+
+
+ The Volume fine.
+
+
+
+
+ The Balance fine.
+
+
+
+
+ The Pan position fine.
+
+
+
+
+ The Expression fine.
+
+
+
+
+ The Effect Control 1 fine.
+
+
+
+
+ The Effect Control 2 fine.
+
+
+
+
+ The Hold Pedal 1.
+
+
+
+
+ The Portamento.
+
+
+
+
+ The Sustenuto Pedal.
+
+
+
+
+ The Soft Pedal.
+
+
+
+
+ The Legato Pedal.
+
+
+
+
+ The Hold Pedal 2.
+
+
+
+
+ The Sound Variation.
+
+
+
+
+ The Sound Timbre.
+
+
+
+
+ The Sound Release Time.
+
+
+
+
+ The Sound Attack Time.
+
+
+
+
+ The Sound Brightness.
+
+
+
+
+ The Sound Control 6.
+
+
+
+
+ The Sound Control 7.
+
+
+
+
+ The Sound Control 8.
+
+
+
+
+ The Sound Control 9.
+
+
+
+
+ The Sound Control 10.
+
+
+
+
+ The General Purpose Button 1.
+
+
+
+
+ The General Purpose Button 2.
+
+
+
+
+ The General Purpose Button 3.
+
+
+
+
+ The General Purpose Button 4.
+
+
+
+
+ The Effects Level.
+
+
+
+
+ The Tremelo Level.
+
+
+
+
+ The Chorus Level.
+
+
+
+
+ The Celeste Level.
+
+
+
+
+ The Phaser Level.
+
+
+
+
+ The Data Button Increment.
+
+
+
+
+ The Data Button Decrement.
+
+
+
+
+ The NonRegistered Parameter Fine.
+
+
+
+
+ The NonRegistered Parameter Coarse.
+
+
+
+
+ The Registered Parameter Fine.
+
+
+
+
+ The Registered Parameter Coarse.
+
+
+
+
+ The All Sound Off.
+
+
+
+
+ The All Controllers Off.
+
+
+
+
+ The Local Keyboard.
+
+
+
+
+ The All Notes Off.
+
+
+
+
+ The Omni Mode Off.
+
+
+
+
+ The Omni Mode On.
+
+
+
+
+ The Mono Operation.
+
+
+
+
+ The Poly Operation.
+
+
+
+
+ Represents MIDI channel messages.
+
+
+
+
+ Represents the basic class for all MIDI short messages.
+
+
+ MIDI short messages represent all MIDI messages except meta messages
+ and system exclusive messages. This includes channel messages, system
+ realtime messages, and system common messages.
+
+
+
+
+ Represents the basic functionality for all MIDI messages.
+
+
+
+
+ Gets the MIDI message's status value.
+
+
+
+
+ Gets the MIDI message's type.
+
+
+
+
+ Gets the MIDI message's status value.
+
+
+
+
+ Gets the MessageType.
+
+
+
+
+ Gets the short message as a packed integer.
+
+
+ The message is packed into an integer value with the low-order byte
+ of the low-word representing the status value. The high-order byte
+ of the low-word represents the first data value, and the low-order
+ byte of the high-word represents the second data value.
+
+
+
+
+ Maximum value allowed for MIDI channels.
+
+
+
+
+ Initializes a new instance of the ChannelMessage class with the
+ specified command, MIDI channel, and data 1 values.
+
+
+ The command value.
+
+
+ The MIDI channel.
+
+
+ The data 1 value.
+
+
+ If midiChannel is less than zero or greater than 15. Or if
+ data1 is less than zero or greater than 127.
+
+
+
+
+ Initializes a new instance of the ChannelMessage class with the
+ specified command, MIDI channel, data 1, and data 2 values.
+
+
+ The command value.
+
+
+ The MIDI channel.
+
+
+ The data 1 value.
+
+
+ The data 2 value.
+
+
+ If midiChannel is less than zero or greater than 15. Or if
+ data1 or data 2 is less than zero or greater than 127.
+
+
+
+
+ Returns a value for the current ChannelMessage suitable for use in
+ hashing algorithms.
+
+
+ A hash code for the current ChannelMessage.
+
+
+
+
+ Determines whether two ChannelMessage instances are equal.
+
+
+ The ChannelMessage to compare with the current ChannelMessage.
+
+
+ true if the specified ChannelMessage is equal to the current
+ ChannelMessage; otherwise, false.
+
+
+
+
+ Returns a value indicating how many bytes are used for the
+ specified ChannelCommand.
+
+
+ The ChannelCommand value to test.
+
+
+ The number of bytes used for the specified ChannelCommand.
+
+
+
+
+ Unpacks the command value from the specified integer channel
+ message.
+
+
+ The message to unpack.
+
+
+ The command value for the packed message.
+
+
+
+
+ Unpacks the MIDI channel from the specified integer channel
+ message.
+
+
+ The message to unpack.
+
+
+ The MIDI channel for the pack message.
+
+
+
+
+ Packs the MIDI channel into the specified integer message.
+
+
+ The message into which the MIDI channel is packed.
+
+
+ The MIDI channel to pack into the message.
+
+
+ An integer message.
+
+
+ If midiChannel is less than zero or greater than 15.
+
+
+
+
+ Packs the command value into an integer message.
+
+
+ The message into which the command is packed.
+
+
+ The command value to pack into the message.
+
+
+ An integer message.
+
+
+
+
+ Gets the channel command value.
+
+
+
+
+ Gets the MIDI channel.
+
+
+
+
+ Gets the first data value.
+
+
+
+
+ Gets the second data value.
+
+
+
+
+ Gets the ChannelMessage's status value.
+
+
+
+
+ Gets the ChannelMessage as a packed integer.
+
+
+
+
+ Gets the ChanngelMessage's MessageType.
+
+
+
+
+ Provides data for ChannelMessage events.
+
+
+
+
+ Initializes a new instance of the ChannelMessageEventArgs class with the
+ specified ChannelMessage and time stamp.
+
+
+ The ChannelMessage for this event.
+
+
+
+
+ Gets the ChannelMessage for this event.
+
+
+
+
+ Represents constant values for MIDI message types.
+
+
+
+
+ Represents MetaMessage types.
+
+
+
+
+ Represents sequencer number type.
+
+
+
+
+ Represents the text type.
+
+
+
+
+ Represents the copyright type.
+
+
+
+
+ Represents the track name type.
+
+
+
+
+ Represents the instrument name type.
+
+
+
+
+ Represents the lyric type.
+
+
+
+
+ Represents the marker type.
+
+
+
+
+ Represents the cue point type.
+
+
+
+
+ Represents the program name type.
+
+
+
+
+ Represents the device name type.
+
+
+
+
+ Represents then end of track type.
+
+
+
+
+ Represents the tempo type.
+
+
+
+
+ Represents the Smpte offset type.
+
+
+
+
+ Represents the time signature type.
+
+
+
+
+ Represents the key signature type.
+
+
+
+
+ Represents the proprietary event type.
+
+
+
+
+ Represents MIDI meta messages.
+
+
+ Meta messages are MIDI messages that are stored in MIDI files. These
+ messages are not sent or received via MIDI but are read and
+ interpretted from MIDI files. They provide information that describes
+ a MIDI file's properties. For example, tempo changes are implemented
+ using meta messages.
+
+
+
+
+ The amount to shift data bytes when calculating the hash code.
+
+
+
+
+ Length in bytes for tempo meta message data.
+
+
+
+
+ Length in bytes for SMPTE offset meta message data.
+
+
+
+
+ Length in bytes for time signature meta message data.
+
+
+
+
+ Length in bytes for key signature meta message data.
+
+
+
+
+ End of track meta message.
+
+
+
+
+ Initializes a new instance of the MetaMessage class.
+
+
+ The type of MetaMessage.
+
+
+ The MetaMessage data.
+
+
+ The length of the MetaMessage is not valid for the MetaMessage type.
+
+
+ Each MetaMessage has type and length properties. For certain
+ types, the length of the message data must be a specific value. For
+ example, tempo messages must have a data length of exactly three.
+ Some MetaMessage types can have any data length. Text messages are
+ an example of a MetaMessage that can have a variable data length.
+ When a MetaMessage is created, the length of the data is checked
+ to make sure that it is valid for the specified type. If it is not,
+ an exception is thrown.
+
+
+
+
+ Gets a copy of the data bytes for this meta message.
+
+
+ A copy of the data bytes for this meta message.
+
+
+
+
+ Returns a value for the current MetaMessage suitable for use in
+ hashing algorithms.
+
+
+ A hash code for the current MetaMessage.
+
+
+
+
+ Determines whether two MetaMessage instances are equal.
+
+
+ The MetaMessage to compare with the current MetaMessage.
+
+
+ true if the specified MetaMessage is equal to the current
+ MetaMessage; otherwise, false.
+
+
+
+
+ Validates data length.
+
+
+ The MetaMessage type.
+
+
+ The length of the MetaMessage data.
+
+
+ true if the data length is valid for this type of
+ MetaMessage; otherwise, false.
+
+
+
+
+ Gets the element at the specified index.
+
+
+ index is less than zero or greater than or equal to Length.
+
+
+
+
+ Gets the length of the meta message.
+
+
+
+
+ Gets the type of meta message.
+
+
+
+
+ Gets the status value.
+
+
+
+
+ Gets the MetaMessage's MessageType.
+
+
+
+
+ Provides data for MetaMessage events.
+
+
+
+
+ Initializes a new instance of the MetaMessageEventArgs class with the
+ specified MetaMessage.
+
+
+ The MetaMessage for this event.
+
+
+
+
+ Gets the MetaMessage for this event.
+
+
+
+
+ Represents the method that handles SysCommonMessage events.
+
+
+
+
+ Defines constants representing the various system common message types.
+
+
+
+
+ Represents the MTC system common message type.
+
+
+
+
+ Represents the song position pointer type.
+
+
+
+
+ Represents the song select type.
+
+
+
+
+ Represents the tune request type.
+
+
+
+
+ Represents MIDI system common messages.
+
+
+
+
+ Initializes a new instance of the SysCommonMessage class with the
+ specified type.
+
+
+ The type of SysCommonMessage.
+
+
+
+
+ Initializes a new instance of the SysCommonMessage class with the
+ specified type and the first data value.
+
+
+ The type of SysCommonMessage.
+
+
+ The first data value.
+
+
+ If data1 is less than zero or greater than 127.
+
+
+
+
+ Initializes a new instance of the SysCommonMessage class with the
+ specified type, first data value, and second data value.
+
+
+ The type of SysCommonMessage.
+
+
+ The first data value.
+
+
+ The second data value.
+
+
+ If data1 or data2 is less than zero or greater than 127.
+
+
+
+
+ Returns a value for the current SysCommonMessage suitable for use
+ in hashing algorithms.
+
+
+ A hash code for the current SysCommonMessage.
+
+
+
+
+ Determines whether two SysCommonMessage instances are equal.
+
+
+ The SysCommonMessage to compare with the current SysCommonMessage.
+
+
+ true if the specified SysCommonMessage is equal to the
+ current SysCommonMessage; otherwise, false.
+
+
+
+
+ Gets the SysCommonType.
+
+
+
+
+ Gets the first data value.
+
+
+
+
+ Gets the second data value.
+
+
+
+
+ Gets the status value.
+
+
+
+
+ Gets the SysCommonMessage as a packed integer.
+
+
+
+
+ Gets the MessageType.
+
+
+
+
+ Represents data for SysCommonMessage events.
+
+
+
+
+ Initializes a new instance of the SysCommonMessageEventArgs class with the
+ specified SysCommonMessage.
+
+
+ The SysCommonMessage for this event.
+
+
+
+
+ Gets the SysCommonMessage for this event.
+
+
+
+
+ Defines constants representing various system exclusive message types.
+
+
+
+
+ Represents the start of system exclusive message type.
+
+
+
+
+ Represents the continuation of a system exclusive message.
+
+
+
+
+ Represents the method that handles SysExMessage events.
+
+
+
+
+ Represents MIDI system exclusive messages.
+
+
+
+
+ Maximum value for system exclusive channels.
+
+
+
+
+ Initializes a new instance of the SysExMessage class with the
+ specified system exclusive data.
+
+
+ The system exclusive data.
+
+
+ The system exclusive data's status byte, the first byte in the
+ data, must have a value of 0xF0 or 0xF7.
+
+
+
+
+ Gets the element at the specified index.
+
+
+ If index is less than zero or greater than or equal to the length
+ of the message.
+
+
+
+
+ Gets the length of the system exclusive data.
+
+
+
+
+ Gets the system exclusive type.
+
+
+
+
+ Gets the status value.
+
+
+
+
+ Gets the MessageType.
+
+
+
+
+ Provides data for SysExMessage events.
+
+
+
+
+ Initializes a new instance of the SysExMessageEventArgs class with the
+ specified system exclusive message and the time stamp.
+
+
+ The SysExMessage for this event.
+
+
+
+
+ Gets the system exclusive message for this event.
+
+
+
+
+ Represents the method that handles SysRealtimeMessage events.
+
+
+
+
+ Defines constants representing the various system realtime message types.
+
+
+
+
+ Represents the clock system realtime type.
+
+
+
+
+ Represents the tick system realtime type.
+
+
+
+
+ Represents the start system realtime type.
+
+
+
+
+ Represents the continue system realtime type.
+
+
+
+
+ Represents the stop system realtime type.
+
+
+
+
+ Represents the active sense system realtime type.
+
+
+
+
+ Represents the reset system realtime type.
+
+
+
+
+ Represents MIDI system realtime messages.
+
+
+ System realtime messages are MIDI messages that are primarily concerned
+ with controlling and synchronizing MIDI devices.
+
+
+
+
+ The instance of the system realtime start message.
+
+
+
+
+ The instance of the system realtime continue message.
+
+
+
+
+ The instance of the system realtime stop message.
+
+
+
+
+ The instance of the system realtime clock message.
+
+
+
+
+ The instance of the system realtime tick message.
+
+
+
+
+ The instance of the system realtime active sense message.
+
+
+
+
+ The instance of the system realtime reset message.
+
+
+
+
+ Returns a value for the current SysRealtimeMessage suitable for use in
+ hashing algorithms.
+
+
+ A hash code for the current SysRealtimeMessage.
+
+
+
+
+ Determines whether two SysRealtimeMessage instances are equal.
+
+
+ The SysRealtimeMessage to compare with the current SysRealtimeMessage.
+
+
+ true if the specified SysRealtimeMessage is equal to the current
+ SysRealtimeMessage; otherwise, false.
+
+
+
+
+ Gets the SysRealtimeType.
+
+
+
+
+ Gets the status value.
+
+
+
+
+ Gets the system realtime message as a packed integer.
+
+
+
+
+ Gets the MessageType.
+
+
+
+
+ Represents data for SysRealtimeMessage events.
+
+
+
+
+ Initializes a new instance of the SysRealtimeMessageEventArgs class with
+ the specified SysRealtimeMessage.
+
+
+ The SysRealtimeMessage for this event.
+
+
+
+
+ Gets the SysRealtimeMessage for this event.
+
+
+
+
+ Summary description for ChannelChaser.
+
+
+
+
+ Summary description for ChannelCleaner.
+
+
+
+
+ Summary description for ChannelStopper.
+
+
+
+
+ Represents functionality for connecting to and disconnecting from an
+ IMetaSource.
+
+
+
+
+ Connects the IMetaSink to the specified IMetaSource.
+
+
+ The IMetaSource to which to connect.
+
+
+
+
+ Disconnects the IMetaSink from the specified IMetaSource.
+
+
+ The IMetaSource from which to disconnect.
+
+
+
+
+ Represents a source of MetaMessages.
+
+
+
+
+ Occurs when a MetaMessage is received, generated, or
+ encountered by a IMetaSource.
+
+
+
+
+ Represents functionality for connecting to and disconnecting from an
+ IMidiEventSource.
+
+
+
+
+ Connects the IMidiEventSink to the specified IMidiEventSource.
+
+
+ The IMidiEventSource to which to connect.
+
+
+
+
+ Disconnects the IMidiEventSink from the specified IMidiEventSource.
+
+
+ The IMidiEventSource from which to disconnect.
+
+
+
+
+ Represents a source of MidiEvents.
+
+
+
+
+ Occurs when a MidiEvent is received, generated, or
+ encountered by a IMidiEventSource.
+
+
+
+
+ Summary description for MidiEventCollection.
+
+
+
+
+ Removes any orphaned note/pedal-on or note/pedal-off messages
+ from the Track.
+
+
+
+
+ Moves a MidiEvent by the specified amount.
+
+
+ The index of the MidiEvent to move.
+
+
+ The number of ticks to move the MidiEvent.
+
+
+
+
+ Merges the specified Track with the current Track.
+
+
+ The Track to merge with.
+
+
+
+
+ Summary description for Track.
+
+
+
+
+ Initializes a new instance of the Track class.
+
+
+
+
+ Summary description for TrackReader.
+
+
+
+
+ Summary description for TrackWriter.
+
+
+
+
+ Summary description for IClock.
+
+
+
+
+ Represents the method for handling Midi events.
+
+
+
+
+ Represents a time-stamped MIDI event.
+
+
+
+
+ Initializes a new instance of the Midi event struct with the
+ specified Midi message and the number of ticks for this event.
+
+
+ The Midi message for the event.
+
+
+ The delta tick value for the event.
+
+
+
+
+ Gets the Midi message for the Midi event.
+
+
+
+
+ Gets or sets the ticks for the Midi event.
+
+
+ Thrown if the ticks value is set to a negative number.
+
+
+
+
+ Provides data for Midi events.
+
+
+
+
+ Initializes a new instance of the MidiEventArgs class with the
+ specified Midi event.
+
+
+ The Midi event for this event.
+
+
+
+
+ Gets the Midi event for this event.
+
+
+
+
+ Summary description for MidiFileProperties.
+
+
+
+
+ Summary description for MidiInternalClock.
+
+
+
+
+ Summary description for PpqnClock.
+
+
+
+
+ Summary description for Sequence.
+
+
+
+
+ Defines constants representing the General MIDI instrument set.
+
+
+
+
+ Converts a Midi note number to its corresponding frequency.
+
+
+
+
+ Converts note to frequency.
+
+
+ The number of the note to convert.
+
+
+ The frequency of the specified note.
+
+
+
+
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/GeneralMidi.cs b/DependenciesCode/Sanford.Multimedia.Midi/GeneralMidi.cs
new file mode 100644
index 0000000..b5da337
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/GeneralMidi.cs
@@ -0,0 +1,171 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Defines constants representing the General MIDI instrument set.
+ ///
+ public enum GeneralMidiInstrument
+ {
+ AcousticGrandPiano,
+ BrightAcousticPiano,
+ ElectricGrandPiano,
+ HonkyTonkPiano,
+ ElectricPiano1,
+ ElectricPiano2,
+ Harpsichord,
+ Clavinet,
+ Celesta,
+ Glockenspiel,
+ MusicBox,
+ Vibraphone,
+ Marimba,
+ Xylophone,
+ TubularBells,
+ Dulcimer,
+ DrawbarOrgan,
+ PercussiveOrgan,
+ RockOrgan,
+ ChurchOrgan,
+ ReedOrgan,
+ Accordion,
+ Harmonica,
+ TangoAccordion,
+ AcousticGuitarNylon,
+ AcousticGuitarSteel,
+ ElectricGuitarJazz,
+ ElectricGuitarClean,
+ ElectricGuitarMuted,
+ OverdrivenGuitar,
+ DistortionGuitar,
+ GuitarHarmonics,
+ AcousticBass,
+ ElectricBassFinger,
+ ElectricBassPick,
+ FretlessBass,
+ SlapBass1,
+ SlapBass2,
+ SynthBass1,
+ SynthBass2,
+ Violin,
+ Viola,
+ Cello,
+ Contrabass,
+ TremoloStrings,
+ PizzicatoStrings,
+ OrchestralHarp,
+ Timpani,
+ StringEnsemble1,
+ StringEnsemble2,
+ SynthStrings1,
+ SynthStrings2,
+ ChoirAahs,
+ VoiceOohs,
+ SynthVoice,
+ OrchestraHit,
+ Trumpet,
+ Trombone,
+ Tuba,
+ MutedTrumpet,
+ FrenchHorn,
+ BrassSection,
+ SynthBrass1,
+ SynthBrass2,
+ SopranoSax,
+ AltoSax,
+ TenorSax,
+ BaritoneSax,
+ Oboe,
+ EnglishHorn,
+ Bassoon,
+ Clarinet,
+ Piccolo,
+ Flute,
+ Recorder,
+ PanFlute,
+ BlownBottle,
+ Shakuhachi,
+ Whistle,
+ Ocarina,
+ Lead1Square,
+ Lead2Sawtooth,
+ Lead3Calliope,
+ Lead4Chiff,
+ Lead5Charang,
+ Lead6Voice,
+ Lead7Fifths,
+ Lead8BassAndLead,
+ Pad1NewAge,
+ Pad2Warm,
+ Pad3Polysynth,
+ Pad4Choir,
+ Pad5Bowed,
+ Pad6Metallic,
+ Pad7Halo,
+ Pad8Sweep,
+ Fx1Rain,
+ Fx2Soundtrack,
+ Fx3Crystal,
+ Fx4Atmosphere,
+ Fx5Brightness,
+ Fx6Goblins,
+ Fx7Echoes,
+ Fx8SciFi,
+ Sitar,
+ Banjo,
+ Shamisen,
+ Koto,
+ Kalimba,
+ BagPipe,
+ Fiddle,
+ Shanai,
+ TinkleBell,
+ Agogo,
+ SteelDrums,
+ Woodblock,
+ TaikoDrum,
+ MelodicTom,
+ SynthDrum,
+ ReverseCymbal,
+ GuitarFretNoise,
+ BreathNoise,
+ Seashore,
+ BirdTweet,
+ TelephoneRing,
+ Helicopter,
+ Applause,
+ Gunshot
+ }
+}
\ No newline at end of file
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/ChannelMessage.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/ChannelMessage.cs
new file mode 100644
index 0000000..09634f6
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/ChannelMessage.cs
@@ -0,0 +1,746 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+
+namespace Sanford.Multimedia.Midi
+{
+ #region Channel Command Types
+
+ ///
+ /// Defines constants for ChannelMessage types.
+ ///
+ public enum ChannelCommand
+ {
+ ///
+ /// Represents the note-off command type.
+ ///
+ NoteOff = 0x80,
+
+ ///
+ /// Represents the note-on command type.
+ ///
+ NoteOn = 0x90,
+
+ ///
+ /// Represents the poly pressure (aftertouch) command type.
+ ///
+ PolyPressure = 0xA0,
+
+ ///
+ /// Represents the controller command type.
+ ///
+ Controller = 0xB0,
+
+ ///
+ /// Represents the program change command type.
+ ///
+ ProgramChange = 0xC0,
+
+ ///
+ /// Represents the channel pressure (aftertouch) command
+ /// type.
+ ///
+ ChannelPressure = 0xD0,
+
+ ///
+ /// Represents the pitch wheel command type.
+ ///
+ PitchWheel = 0xE0
+ }
+
+ #endregion
+
+ #region Controller Types
+
+ ///
+ /// Defines constants for controller types.
+ ///
+ public enum ControllerType
+ {
+ ///
+ /// The Bank Select coarse.
+ ///
+ BankSelect,
+
+ ///
+ /// The Modulation Wheel coarse.
+ ///
+ ModulationWheel,
+
+ ///
+ /// The Breath Control coarse.
+ ///
+ BreathControl,
+
+ ///
+ /// The Foot Pedal coarse.
+ ///
+ FootPedal = 4,
+
+ ///
+ /// The Portamento Time coarse.
+ ///
+ PortamentoTime,
+
+ ///
+ /// The Data Entry Slider coarse.
+ ///
+ DataEntrySlider,
+
+ ///
+ /// The Volume coarse.
+ ///
+ Volume,
+
+ ///
+ /// The Balance coarse.
+ ///
+ Balance,
+
+ ///
+ /// The Pan position coarse.
+ ///
+ Pan = 10,
+
+ ///
+ /// The Expression coarse.
+ ///
+ Expression,
+
+ ///
+ /// The Effect Control 1 coarse.
+ ///
+ EffectControl1,
+
+ ///
+ /// The Effect Control 2 coarse.
+ ///
+ EffectControl2,
+
+ ///
+ /// The General Puprose Slider 1
+ ///
+ GeneralPurposeSlider1 = 16,
+
+ ///
+ /// The General Puprose Slider 2
+ ///
+ GeneralPurposeSlider2,
+
+ ///
+ /// The General Puprose Slider 3
+ ///
+ GeneralPurposeSlider3,
+
+ ///
+ /// The General Puprose Slider 4
+ ///
+ GeneralPurposeSlider4,
+
+ ///
+ /// The Bank Select fine.
+ ///
+ BankSelectFine = 32,
+
+ ///
+ /// The Modulation Wheel fine.
+ ///
+ ModulationWheelFine,
+
+ ///
+ /// The Breath Control fine.
+ ///
+ BreathControlFine,
+
+ ///
+ /// The Foot Pedal fine.
+ ///
+ FootPedalFine = 36,
+
+ ///
+ /// The Portamento Time fine.
+ ///
+ PortamentoTimeFine,
+
+ ///
+ /// The Data Entry Slider fine.
+ ///
+ DataEntrySliderFine,
+
+ ///
+ /// The Volume fine.
+ ///
+ VolumeFine,
+
+ ///
+ /// The Balance fine.
+ ///
+ BalanceFine,
+
+ ///
+ /// The Pan position fine.
+ ///
+ PanFine = 42,
+
+ ///
+ /// The Expression fine.
+ ///
+ ExpressionFine,
+
+ ///
+ /// The Effect Control 1 fine.
+ ///
+ EffectControl1Fine,
+
+ ///
+ /// The Effect Control 2 fine.
+ ///
+ EffectControl2Fine,
+
+ ///
+ /// The Hold Pedal 1.
+ ///
+ HoldPedal1 = 64,
+
+ ///
+ /// The Portamento.
+ ///
+ Portamento,
+
+ ///
+ /// The Sustenuto Pedal.
+ ///
+ SustenutoPedal,
+
+ ///
+ /// The Soft Pedal.
+ ///
+ SoftPedal,
+
+ ///
+ /// The Legato Pedal.
+ ///
+ LegatoPedal,
+
+ ///
+ /// The Hold Pedal 2.
+ ///
+ HoldPedal2,
+
+ ///
+ /// The Sound Variation.
+ ///
+ SoundVariation,
+
+ ///
+ /// The Sound Timbre.
+ ///
+ SoundTimbre,
+
+ ///
+ /// The Sound Release Time.
+ ///
+ SoundReleaseTime,
+
+ ///
+ /// The Sound Attack Time.
+ ///
+ SoundAttackTime,
+
+ ///
+ /// The Sound Brightness.
+ ///
+ SoundBrightness,
+
+ ///
+ /// The Sound Control 6.
+ ///
+ SoundControl6,
+
+ ///
+ /// The Sound Control 7.
+ ///
+ SoundControl7,
+
+ ///
+ /// The Sound Control 8.
+ ///
+ SoundControl8,
+
+ ///
+ /// The Sound Control 9.
+ ///
+ SoundControl9,
+
+ ///
+ /// The Sound Control 10.
+ ///
+ SoundControl10,
+
+ ///
+ /// The General Purpose Button 1.
+ ///
+ GeneralPurposeButton1,
+
+ ///
+ /// The General Purpose Button 2.
+ ///
+ GeneralPurposeButton2,
+
+ ///
+ /// The General Purpose Button 3.
+ ///
+ GeneralPurposeButton3,
+
+ ///
+ /// The General Purpose Button 4.
+ ///
+ GeneralPurposeButton4,
+
+ ///
+ /// The Effects Level.
+ ///
+ EffectsLevel = 91,
+
+ ///
+ /// The Tremelo Level.
+ ///
+ TremeloLevel,
+
+ ///
+ /// The Chorus Level.
+ ///
+ ChorusLevel,
+
+ ///
+ /// The Celeste Level.
+ ///
+ CelesteLevel,
+
+ ///
+ /// The Phaser Level.
+ ///
+ PhaserLevel,
+
+ ///
+ /// The Data Button Increment.
+ ///
+ DataButtonIncrement,
+
+ ///
+ /// The Data Button Decrement.
+ ///
+ DataButtonDecrement,
+
+ ///
+ /// The NonRegistered Parameter Fine.
+ ///
+ NonRegisteredParameterFine,
+
+ ///
+ /// The NonRegistered Parameter Coarse.
+ ///
+ NonRegisteredParameterCoarse,
+
+ ///
+ /// The Registered Parameter Fine.
+ ///
+ RegisteredParameterFine,
+
+ ///
+ /// The Registered Parameter Coarse.
+ ///
+ RegisteredParameterCoarse,
+
+ ///
+ /// The All Sound Off.
+ ///
+ AllSoundOff = 120,
+
+ ///
+ /// The All Controllers Off.
+ ///
+ AllControllersOff,
+
+ ///
+ /// The Local Keyboard.
+ ///
+ LocalKeyboard,
+
+ ///
+ /// The All Notes Off.
+ ///
+ AllNotesOff,
+
+ ///
+ /// The Omni Mode Off.
+ ///
+ OmniModeOff,
+
+ ///
+ /// The Omni Mode On.
+ ///
+ OmniModeOn,
+
+ ///
+ /// The Mono Operation.
+ ///
+ MonoOperation,
+
+ ///
+ /// The Poly Operation.
+ ///
+ PolyOperation
+ }
+
+ #endregion
+
+ ///
+ /// Represents MIDI channel messages.
+ ///
+ [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;
+
+ ///
+ /// Maximum value allowed for MIDI channels.
+ ///
+ public const int MidiChannelMaxValue = 15;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the ChannelEventArgs class with the
+ /// specified command, MIDI channel, and data 1 values.
+ ///
+ ///
+ /// The command value.
+ ///
+ ///
+ /// The MIDI channel.
+ ///
+ ///
+ /// The data 1 value.
+ ///
+ ///
+ /// If midiChannel is less than zero or greater than 15. Or if
+ /// data1 is less than zero or greater than 127.
+ ///
+ 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
+ }
+
+ ///
+ /// Initializes a new instance of the ChannelEventArgs class with the
+ /// specified command, MIDI channel, data 1, and data 2 values.
+ ///
+ ///
+ /// The command value.
+ ///
+ ///
+ /// The MIDI channel.
+ ///
+ ///
+ /// The data 1 value.
+ ///
+ ///
+ /// The data 2 value.
+ ///
+ ///
+ /// If midiChannel is less than zero or greater than 15. Or if
+ /// data1 or data 2 is less than zero or greater than 127.
+ ///
+ 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
+
+ ///
+ /// Returns a value for the current ChannelEventArgs suitable for use in
+ /// hashing algorithms.
+ ///
+ ///
+ /// A hash code for the current ChannelEventArgs.
+ ///
+ public override int GetHashCode()
+ {
+ return msg;
+ }
+
+ ///
+ /// Determines whether two ChannelEventArgs instances are equal.
+ ///
+ ///
+ /// The ChannelMessageEventArgs to compare with the current ChannelEventArgs.
+ ///
+ ///
+ /// true if the specified object is equal to the current
+ /// ChannelMessageEventArgs; otherwise, false.
+ ///
+ public override bool Equals(object obj)
+ {
+ #region Guard
+
+ if(!(obj is ChannelMessage))
+ {
+ return false;
+ }
+
+ #endregion
+
+ ChannelMessage e = (ChannelMessage)obj;
+
+ return this.msg == e.msg;
+ }
+
+ ///
+ /// Returns a value indicating how many bytes are used for the
+ /// specified ChannelCommand.
+ ///
+ ///
+ /// The ChannelCommand value to test.
+ ///
+ ///
+ /// The number of bytes used for the specified ChannelCommand.
+ ///
+ internal static int DataBytesPerType(ChannelCommand command)
+ {
+ int result;
+
+ if(command == ChannelCommand.ChannelPressure ||
+ command == ChannelCommand.ProgramChange)
+ {
+ result = 1;
+ }
+ else
+ {
+ result = 2;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Unpacks the command value from the specified integer channel
+ /// message.
+ ///
+ ///
+ /// The message to unpack.
+ ///
+ ///
+ /// The command value for the packed message.
+ ///
+ internal static ChannelCommand UnpackCommand(int message)
+ {
+ return (ChannelCommand)(message & DataMask & MidiChannelMask);
+ }
+
+ ///
+ /// Unpacks the MIDI channel from the specified integer channel
+ /// message.
+ ///
+ ///
+ /// The message to unpack.
+ ///
+ ///
+ /// The MIDI channel for the pack message.
+ ///
+ internal static int UnpackMidiChannel(int message)
+ {
+ return message & DataMask & CommandMask;
+ }
+
+ ///
+ /// Packs the MIDI channel into the specified integer message.
+ ///
+ ///
+ /// The message into which the MIDI channel is packed.
+ ///
+ ///
+ /// The MIDI channel to pack into the message.
+ ///
+ ///
+ /// An integer message.
+ ///
+ ///
+ /// If midiChannel is less than zero or greater than 15.
+ ///
+ 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;
+ }
+
+ ///
+ /// Packs the command value into an integer message.
+ ///
+ ///
+ /// The message into which the command is packed.
+ ///
+ ///
+ /// The command value to pack into the message.
+ ///
+ ///
+ /// An integer message.
+ ///
+ internal static int PackCommand(int message, ChannelCommand command)
+ {
+ return (message & CommandMask) | (int)command;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the channel command value.
+ ///
+ public ChannelCommand Command
+ {
+ get
+ {
+ return UnpackCommand(msg);
+ }
+ }
+
+ ///
+ /// Gets the MIDI channel.
+ ///
+ public int MidiChannel
+ {
+ get
+ {
+ return UnpackMidiChannel(msg);
+ }
+ }
+
+ ///
+ /// Gets the first data value.
+ ///
+ public int Data1
+ {
+ get
+ {
+ return UnpackData1(msg);
+ }
+ }
+
+ ///
+ /// Gets the second data value.
+ ///
+ public int Data2
+ {
+ get
+ {
+ return UnpackData2(msg);
+ }
+ }
+
+ ///
+ /// Gets the EventType.
+ ///
+ public override MessageType MessageType
+ {
+ get
+ {
+ return MessageType.Channel;
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/ChannelMessageEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/ChannelMessageEventArgs.cs
new file mode 100644
index 0000000..af247f1
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/ChannelMessageEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class ChannelMessageEventArgs : EventArgs
+ {
+ private ChannelMessage message;
+
+ public ChannelMessageEventArgs(ChannelMessage message)
+ {
+ this.message = message;
+ }
+
+ public ChannelMessage Message
+ {
+ get
+ {
+ return message;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/InvalidShortMessageEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/InvalidShortMessageEventArgs.cs
new file mode 100644
index 0000000..ccede1c
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/InvalidShortMessageEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class InvalidShortMessageEventArgs : EventArgs
+ {
+ private int message;
+
+ public InvalidShortMessageEventArgs(int message)
+ {
+ this.message = message;
+ }
+
+ public int Message
+ {
+ get
+ {
+ return message;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/InvalidSysExMessageEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/InvalidSysExMessageEventArgs.cs
new file mode 100644
index 0000000..11b14a7
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/InvalidSysExMessageEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class InvalidSysExMessageEventArgs : EventArgs
+ {
+ private byte[] messageData;
+
+ public InvalidSysExMessageEventArgs(byte[] messageData)
+ {
+ this.messageData = messageData;
+ }
+
+ public ICollection MessageData
+ {
+ get
+ {
+ return messageData;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/MetaMessageEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/MetaMessageEventArgs.cs
new file mode 100644
index 0000000..92e892d
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/MetaMessageEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class MetaMessageEventArgs : EventArgs
+ {
+ private MetaMessage message;
+
+ public MetaMessageEventArgs(MetaMessage message)
+ {
+ this.message = message;
+ }
+
+ public MetaMessage Message
+ {
+ get
+ {
+ return message;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/SysCommonMessageEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/SysCommonMessageEventArgs.cs
new file mode 100644
index 0000000..ad76d6a
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/SysCommonMessageEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class SysCommonMessageEventArgs : EventArgs
+ {
+ private SysCommonMessage message;
+
+ public SysCommonMessageEventArgs(SysCommonMessage message)
+ {
+ this.message = message;
+ }
+
+ public SysCommonMessage Message
+ {
+ get
+ {
+ return message;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/SysExMessageEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/SysExMessageEventArgs.cs
new file mode 100644
index 0000000..5438032
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/SysExMessageEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class SysExMessageEventArgs : EventArgs
+ {
+ private SysExMessage message;
+
+ public SysExMessageEventArgs(SysExMessage message)
+ {
+ this.message = message;
+ }
+
+ public SysExMessage Message
+ {
+ get
+ {
+ return message;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/SysRealtimeMessageEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/SysRealtimeMessageEventArgs.cs
new file mode 100644
index 0000000..32ef726
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/EventArgs/SysRealtimeMessageEventArgs.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class SysRealtimeMessageEventArgs : EventArgs
+ {
+ public static readonly SysRealtimeMessageEventArgs Start = new SysRealtimeMessageEventArgs(SysRealtimeMessage.StartMessage);
+
+ public static readonly SysRealtimeMessageEventArgs Continue = new SysRealtimeMessageEventArgs(SysRealtimeMessage.ContinueMessage);
+
+ public static readonly SysRealtimeMessageEventArgs Stop = new SysRealtimeMessageEventArgs(SysRealtimeMessage.StopMessage);
+
+ public static readonly SysRealtimeMessageEventArgs Clock = new SysRealtimeMessageEventArgs(SysRealtimeMessage.ClockMessage);
+
+ public static readonly SysRealtimeMessageEventArgs Tick = new SysRealtimeMessageEventArgs(SysRealtimeMessage.TickMessage);
+
+ public static readonly SysRealtimeMessageEventArgs ActiveSense = new SysRealtimeMessageEventArgs(SysRealtimeMessage.ActiveSenseMessage);
+
+ public static readonly SysRealtimeMessageEventArgs Reset = new SysRealtimeMessageEventArgs(SysRealtimeMessage.ResetMessage);
+
+ private SysRealtimeMessage message;
+
+ private SysRealtimeMessageEventArgs(SysRealtimeMessage message)
+ {
+ this.message = message;
+ }
+
+ public SysRealtimeMessage Message
+ {
+ get
+ {
+ return message;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/IMidiMessage.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/IMidiMessage.cs
new file mode 100644
index 0000000..d0f03f0
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/IMidiMessage.cs
@@ -0,0 +1,84 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Defines constants representing MIDI message types.
+ ///
+ public enum MessageType
+ {
+ Channel,
+
+ SystemExclusive,
+
+ SystemCommon,
+
+ SystemRealtime,
+
+ Meta
+ }
+
+ ///
+ /// Represents the basic functionality for all MIDI messages.
+ ///
+ public interface IMidiMessage
+ {
+ ///
+ /// Gets a byte array representation of the MIDI message.
+ ///
+ ///
+ /// A byte array representation of the MIDI message.
+ ///
+ byte[] GetBytes();
+
+ ///
+ /// Gets the MIDI message's status value.
+ ///
+ int Status
+ {
+ get;
+ }
+
+ ///
+ /// Gets the MIDI event's type.
+ ///
+ MessageType MessageType
+ {
+ get;
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/ChannelMessageBuilder.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/ChannelMessageBuilder.cs
new file mode 100644
index 0000000..086a923
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/ChannelMessageBuilder.cs
@@ -0,0 +1,256 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Provides functionality for building ChannelMessages.
+ ///
+ public class ChannelMessageBuilder : IMessageBuilder
+ {
+ #region ChannelMessageBuilder Members
+
+ #region Class Fields
+
+ // Stores the ChannelMessages.
+ private static Hashtable messageCache = Hashtable.Synchronized(new Hashtable());
+
+ #endregion
+
+ #region Fields
+
+ // The channel message as a packed integer.
+ private int message = 0;
+
+ // The built ChannelMessage
+ private ChannelMessage result = null;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the ChannelMessageBuilder class.
+ ///
+ public ChannelMessageBuilder()
+ {
+ Command = ChannelCommand.Controller;
+ MidiChannel = 0;
+ Data1 = (int)ControllerType.AllSoundOff;
+ Data2 = 0;
+ }
+
+ ///
+ /// Initializes a new instance of the ChannelMessageBuilder class with
+ /// the specified ChannelMessageEventArgs.
+ ///
+ ///
+ /// The ChannelMessageEventArgs to use for initializing the ChannelMessageBuilder.
+ ///
+ ///
+ /// The ChannelMessageBuilder uses the specified ChannelMessageEventArgs to
+ /// initialize its property values.
+ ///
+ public ChannelMessageBuilder(ChannelMessage message)
+ {
+ Initialize(message);
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initializes the ChannelMessageBuilder with the specified
+ /// ChannelMessageEventArgs.
+ ///
+ ///
+ /// The ChannelMessageEventArgs to use for initializing the ChannelMessageBuilder.
+ ///
+ public void Initialize(ChannelMessage message)
+ {
+ this.message = message.Message;
+ }
+
+ ///
+ /// Clears the ChannelMessageEventArgs cache.
+ ///
+ public static void Clear()
+ {
+ messageCache.Clear();
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the number of messages in the ChannelMessageEventArgs cache.
+ ///
+ public static int Count
+ {
+ get
+ {
+ return messageCache.Count;
+ }
+ }
+
+ ///
+ /// Gets the built ChannelMessageEventArgs.
+ ///
+ public ChannelMessage Result
+ {
+ get
+ {
+ return result;
+ }
+ }
+
+ ///
+ /// Gets or sets the ChannelMessageEventArgs as a packed integer.
+ ///
+ internal int Message
+ {
+ get
+ {
+ return message;
+ }
+ set
+ {
+ message = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the Command value to use for building the
+ /// ChannelMessageEventArgs.
+ ///
+ public ChannelCommand Command
+ {
+ get
+ {
+ return ChannelMessage.UnpackCommand(message);
+ }
+ set
+ {
+ message = ChannelMessage.PackCommand(message, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the MIDI channel to use for building the
+ /// ChannelMessageEventArgs.
+ ///
+ ///
+ /// MidiChannel is set to a value less than zero or greater than 15.
+ ///
+ public int MidiChannel
+ {
+ get
+ {
+ return ChannelMessage.UnpackMidiChannel(message);
+ }
+ set
+ {
+ message = ChannelMessage.PackMidiChannel(message, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the first data value to use for building the
+ /// ChannelMessageEventArgs.
+ ///
+ ///
+ /// Data1 is set to a value less than zero or greater than 127.
+ ///
+ public int Data1
+ {
+ get
+ {
+ return ShortMessage.UnpackData1(message);
+ }
+ set
+ {
+ message = ShortMessage.PackData1(message, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the second data value to use for building the
+ /// ChannelMessageEventArgs.
+ ///
+ ///
+ /// Data2 is set to a value less than zero or greater than 127.
+ ///
+ public int Data2
+ {
+ get
+ {
+ return ShortMessage.UnpackData2(message);
+ }
+ set
+ {
+ message = ShortMessage.PackData2(message, value);
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IMessageBuilder Members
+
+ ///
+ /// Builds a ChannelMessageEventArgs.
+ ///
+ public void Build()
+ {
+ result = (ChannelMessage)messageCache[message];
+
+ // If the message does not exist.
+ if(result == null)
+ {
+ result = new ChannelMessage(message);
+
+ // Add message to cache.
+ messageCache.Add(message, result);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/IMessageBuilder.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/IMessageBuilder.cs
new file mode 100644
index 0000000..ae75c49
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/IMessageBuilder.cs
@@ -0,0 +1,51 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Represents functionality for building MIDI messages.
+ ///
+ public interface IMessageBuilder
+ {
+ #region IMessageBuilder Members
+
+ ///
+ /// Builds the MIDI message.
+ ///
+ void Build();
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/KeySignatureBuilder.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/KeySignatureBuilder.cs
new file mode 100644
index 0000000..b1eb641
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/KeySignatureBuilder.cs
@@ -0,0 +1,424 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using Sanford.Multimedia;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Builds key signature MetaMessages.
+ ///
+ public class KeySignatureBuilder : IMessageBuilder
+ {
+ private Key key = Key.CMajor;
+
+ private MetaMessage result = null;
+
+ ///
+ /// Initializes a new instance of the KeySignatureBuilder class.
+ ///
+ public KeySignatureBuilder()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the KeySignatureBuilder class with
+ /// the specified key signature MetaMessage.
+ ///
+ ///
+ /// The key signature MetaMessage to use for initializing the
+ /// KeySignatureBuilder class.
+ ///
+ public KeySignatureBuilder(MetaMessage message)
+ {
+ Initialize(message);
+ }
+
+ ///
+ /// Initializes the KeySignatureBuilder with the specified MetaMessage.
+ ///
+ ///
+ /// The key signature MetaMessage to use for initializing the
+ /// KeySignatureBuilder.
+ ///
+ public void Initialize(MetaMessage message)
+ {
+ #region Require
+
+ if(message == null)
+ {
+ throw new ArgumentNullException("message");
+ }
+ else if(message.MetaType != MetaType.KeySignature)
+ {
+ throw new ArgumentException("Wrong meta event type.", "messaege");
+ }
+
+ #endregion
+
+ sbyte b = (sbyte)message[0];
+
+ // If the key is major.
+ if(message[1] == 0)
+ {
+ switch(b)
+ {
+ case -7:
+ key = Key.CFlatMajor;
+ break;
+
+ case -6:
+ key = Key.GFlatMajor;
+ break;
+
+ case -5:
+ key = Key.DFlatMajor;
+ break;
+
+ case -4:
+ key = Key.AFlatMajor;
+ break;
+
+ case -3:
+ key = Key.EFlatMajor;
+ break;
+
+ case -2:
+ key = Key.BFlatMajor;
+ break;
+
+ case -1:
+ key = Key.FMajor;
+ break;
+
+ case 0:
+ key = Key.CMajor;
+ break;
+
+ case 1:
+ key = Key.GMajor;
+ break;
+
+ case 2:
+ key = Key.DMajor;
+ break;
+
+ case 3:
+ key = Key.AMajor;
+ break;
+
+ case 4:
+ key = Key.EMajor;
+ break;
+
+ case 5:
+ key = Key.BMajor;
+ break;
+
+ case 6:
+ key = Key.FSharpMajor;
+ break;
+
+ case 7:
+ key = Key.CSharpMajor;
+ break;
+ }
+
+ }
+ // Else the key is minor.
+ else
+ {
+ switch(b)
+ {
+ case -7:
+ key = Key.AFlatMinor;
+ break;
+
+ case -6:
+ key = Key.EFlatMinor;
+ break;
+
+ case -5:
+ key = Key.BFlatMinor;
+ break;
+
+ case -4:
+ key = Key.FMinor;
+ break;
+
+ case -3:
+ key = Key.CMinor;
+ break;
+
+ case -2:
+ key = Key.GMinor;
+ break;
+
+ case -1:
+ key = Key.DMinor;
+ break;
+
+ case 0:
+ key = Key.AMinor;
+ break;
+
+ case 1:
+ key = Key.EMinor;
+ break;
+
+ case 2:
+ key = Key.BMinor;
+ break;
+
+ case 3:
+ key = Key.FSharpMinor;
+ break;
+
+ case 4:
+ key = Key.CSharpMinor;
+ break;
+
+ case 5:
+ key = Key.GSharpMinor;
+ break;
+
+ case 6:
+ key = Key.DSharpMinor;
+ break;
+
+ case 7:
+ key = Key.ASharpMinor;
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Gets or sets the key.
+ ///
+ public Key Key
+ {
+ get
+ {
+ return key;
+ }
+ set
+ {
+ key = value;
+ }
+ }
+
+ ///
+ /// The build key signature MetaMessage.
+ ///
+ public MetaMessage Result
+ {
+ get
+ {
+ return result;
+ }
+ }
+
+ #region IMessageBuilder Members
+
+ ///
+ /// Builds the key signature MetaMessage.
+ ///
+ public void Build()
+ {
+ byte[] data = new byte[MetaMessage.KeySigLength];
+
+ unchecked
+ {
+ switch(Key)
+ {
+ case Key.CFlatMajor:
+ data[0] = (byte)-7;
+ data[1] = 0;
+ break;
+
+ case Key.GFlatMajor:
+ data[0] = (byte)-6;
+ data[1] = 0;
+ break;
+
+ case Key.DFlatMajor:
+ data[0] = (byte)-5;
+ data[1] = 0;
+ break;
+
+ case Key.AFlatMajor:
+ data[0] = (byte)-4;
+ data[1] = 0;
+ break;
+
+ case Key.EFlatMajor:
+ data[0] = (byte)-3;
+ data[1] = 0;
+ break;
+
+ case Key.BFlatMajor:
+ data[0] = (byte)-2;
+ data[1] = 0;
+ break;
+
+ case Key.FMajor:
+ data[0] = (byte)-1;
+ data[1] = 0;
+ break;
+
+ case Key.CMajor:
+ data[0] = 0;
+ data[1] = 0;
+ break;
+
+ case Key.GMajor:
+ data[0] = 1;
+ data[1] = 0;
+ break;
+
+ case Key.DMajor:
+ data[0] = 2;
+ data[1] = 0;
+ break;
+
+ case Key.AMajor:
+ data[0] = 3;
+ data[1] = 0;
+ break;
+
+ case Key.EMajor:
+ data[0] = 4;
+ data[1] = 0;
+ break;
+
+ case Key.BMajor:
+ data[0] = 5;
+ data[1] = 0;
+ break;
+
+ case Key.FSharpMajor:
+ data[0] = 6;
+ data[1] = 0;
+ break;
+
+ case Key.CSharpMajor:
+ data[0] = 7;
+ data[1] = 0;
+ break;
+
+ case Key.AFlatMinor:
+ data[0] = (byte)-7;
+ data[1] = 1;
+ break;
+
+ case Key.EFlatMinor:
+ data[0] = (byte)-6;
+ data[1] = 1;
+ break;
+
+ case Key.BFlatMinor:
+ data[0] = (byte)-5;
+ data[1] = 1;
+ break;
+
+ case Key.FMinor:
+ data[0] = (byte)-4;
+ data[1] = 1;
+ break;
+
+ case Key.CMinor:
+ data[0] = (byte)-3;
+ data[1] = 1;
+ break;
+
+ case Key.GMinor:
+ data[0] = (byte)-2;
+ data[1] = 1;
+ break;
+
+ case Key.DMinor:
+ data[0] = (byte)-1;
+ data[1] = 1;
+ break;
+
+ case Key.AMinor:
+ data[0] = 1;
+ data[1] = 0;
+ break;
+
+ case Key.EMinor:
+ data[0] = 1;
+ data[1] = 1;
+ break;
+
+ case Key.BMinor:
+ data[0] = 2;
+ data[1] = 1;
+ break;
+
+ case Key.FSharpMinor:
+ data[0] = 3;
+ data[1] = 1;
+ break;
+
+ case Key.CSharpMinor:
+ data[0] = 4;
+ data[1] = 1;
+ break;
+
+ case Key.GSharpMinor:
+ data[0] = 5;
+ data[1] = 1;
+ break;
+
+ case Key.DSharpMinor:
+ data[0] = 6;
+ data[1] = 1;
+ break;
+
+ case Key.ASharpMinor:
+ data[0] = 7;
+ data[1] = 1;
+ break;
+ }
+ }
+
+ result = new MetaMessage(MetaType.KeySignature, data);
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/MetaTextBuilder.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/MetaTextBuilder.cs
new file mode 100644
index 0000000..e980961
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/MetaTextBuilder.cs
@@ -0,0 +1,416 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Provides functionality for building meta text messages.
+ ///
+ public class MetaTextBuilder : IMessageBuilder
+ {
+ #region MetaTextBuilder Members
+
+ #region Fields
+
+ // The text represented by the MetaMessage.
+ private string text;
+
+ // The MetaMessage type - must be one of the text based types.
+ private MetaType type = MetaType.Text;
+
+ // The built MetaMessage.
+ private MetaMessage result = null;
+
+ // Indicates whether or not the text has changed since the message was
+ // last built.
+ private bool changed = true;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the MetaMessageTextBuilder class.
+ ///
+ public MetaTextBuilder()
+ {
+ text = string.Empty;
+ }
+
+ ///
+ /// Initializes a new instance of the MetaMessageTextBuilder class with the
+ /// specified type.
+ ///
+ ///
+ /// The type of MetaMessage.
+ ///
+ ///
+ /// If the MetaMessage type is not a text based type.
+ ///
+ ///
+ /// The MetaMessage type must be one of the following text based
+ /// types:
+ ///
+ /// -
+ /// Copyright
+ ///
+ /// -
+ /// Cuepoint
+ ///
+ /// -
+ /// DeviceName
+ ///
+ /// -
+ /// InstrumentName
+ ///
+ /// -
+ /// Lyric
+ ///
+ /// -
+ /// Marker
+ ///
+ /// -
+ /// ProgramName
+ ///
+ /// -
+ /// Text
+ ///
+ /// -
+ /// TrackName
+ ///
+ ///
+ /// If the MetaMessage is not a text based type, an exception
+ /// will be thrown.
+ ///
+ public MetaTextBuilder(MetaType type)
+ {
+ #region Require
+
+ if(!IsTextType(type))
+ {
+ throw new ArgumentException("Not text based meta message type.",
+ "message");
+ }
+
+ #endregion
+
+ this.text = string.Empty;
+ }
+
+ ///
+ /// Initializes a new instance of the MetaMessageTextBuilder class with the
+ /// specified type.
+ ///
+ ///
+ /// The type of MetaMessage.
+ ///
+ ///
+ /// If the MetaMessage type is not a text based type.
+ ///
+ ///
+ /// The MetaMessage type must be one of the following text based
+ /// types:
+ ///
+ /// -
+ /// Copyright
+ ///
+ /// -
+ /// Cuepoint
+ ///
+ /// -
+ /// DeviceName
+ ///
+ /// -
+ /// InstrumentName
+ ///
+ /// -
+ /// Lyric
+ ///
+ /// -
+ /// Marker
+ ///
+ /// -
+ /// ProgramName
+ ///
+ /// -
+ /// Text
+ ///
+ /// -
+ /// TrackName
+ ///
+ ///
+ /// If the MetaMessage is not a text based type, an exception
+ /// will be thrown.
+ ///
+ public MetaTextBuilder(MetaType type, string text)
+ {
+ #region Require
+
+ if(!IsTextType(type))
+ {
+ throw new ArgumentException("Not text based meta message type.",
+ "message");
+ }
+
+ #endregion
+
+ this.type = type;
+
+ if(text != null)
+ {
+ this.text = text;
+ }
+ else
+ {
+ this.text = string.Empty;
+ }
+ }
+
+
+ ///
+ /// Initializes a new instance of the MetaMessageTextBuilder class with the
+ /// specified MetaMessage.
+ ///
+ ///
+ /// The MetaMessage to use for initializing the MetaMessageTextBuilder.
+ ///
+ ///
+ /// If the MetaMessage is not a text based type.
+ ///
+ ///
+ /// The MetaMessage must be one of the following text based types:
+ ///
+ /// -
+ /// Copyright
+ ///
+ /// -
+ /// Cuepoint
+ ///
+ /// -
+ /// DeviceName
+ ///
+ /// -
+ /// InstrumentName
+ ///
+ /// -
+ /// Lyric
+ ///
+ /// -
+ /// Marker
+ ///
+ /// -
+ /// ProgramName
+ ///
+ /// -
+ /// Text
+ ///
+ /// -
+ /// TrackName
+ ///
+ ///
+ /// If the MetaMessage is not a text based type, an exception will be
+ /// thrown.
+ ///
+ public MetaTextBuilder(MetaMessage message)
+ {
+ Initialize(message);
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initializes the MetaMessageTextBuilder with the specified MetaMessage.
+ ///
+ ///
+ /// The MetaMessage to use for initializing the MetaMessageTextBuilder.
+ ///
+ ///
+ /// If the MetaMessage is not a text based type.
+ ///
+ public void Initialize(MetaMessage message)
+ {
+ #region Require
+
+ if(!IsTextType(message.MetaType))
+ {
+ throw new ArgumentException("Not text based meta message.",
+ "message");
+ }
+
+ #endregion
+
+ ASCIIEncoding encoding = new ASCIIEncoding();
+
+ text = encoding.GetString(message.GetBytes());
+ this.type = message.MetaType;
+ }
+
+ ///
+ /// Indicates whether or not the specified MetaType is a text based
+ /// type.
+ ///
+ ///
+ /// The MetaType to test.
+ ///
+ ///
+ /// true if the MetaType is a text based type;
+ /// otherwise, false.
+ ///
+ private bool IsTextType(MetaType type)
+ {
+ bool result;
+
+ if(type == MetaType.Copyright ||
+ type == MetaType.CuePoint ||
+ type == MetaType.DeviceName ||
+ type == MetaType.InstrumentName ||
+ type == MetaType.Lyric ||
+ type == MetaType.Marker ||
+ type == MetaType.ProgramName ||
+ type == MetaType.Text ||
+ type == MetaType.TrackName)
+ {
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets or sets the text for the MetaMessage.
+ ///
+ public string Text
+ {
+ get
+ {
+ return text;
+ }
+ set
+ {
+ if(value != null)
+ {
+ text = value;
+ }
+ else
+ {
+ text = string.Empty;
+ }
+
+ changed = true;
+ }
+ }
+
+ ///
+ /// Gets or sets the MetaMessage type.
+ ///
+ ///
+ /// If the type is not a text based type.
+ ///
+ public MetaType Type
+ {
+ get
+ {
+ return type;
+ }
+ set
+ {
+ #region Require
+
+ if(!IsTextType(value))
+ {
+ throw new ArgumentException("Not text based meta message type.",
+ "message");
+ }
+
+ #endregion
+
+ type = value;
+
+ changed = true;
+ }
+ }
+
+ ///
+ /// Gets the built MetaMessage.
+ ///
+ public MetaMessage Result
+ {
+ get
+ {
+ return result;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IMessageBuilder Members
+
+ ///
+ /// Builds the text MetaMessage.
+ ///
+ public void Build()
+ {
+ // If the text has changed since the last time this method was
+ // called.
+ if(changed)
+ {
+ //
+ // Build text MetaMessage.
+ //
+
+ ASCIIEncoding encoding = new ASCIIEncoding();
+ byte[] data = encoding.GetBytes(text);
+ result = new MetaMessage(Type, data);
+ changed = false;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/SongPositionPointerBuilder.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/SongPositionPointerBuilder.cs
new file mode 100644
index 0000000..4222db0
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/SongPositionPointerBuilder.cs
@@ -0,0 +1,266 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Provides functionality for building song position pointer messages.
+ ///
+ public class SongPositionPointerBuilder : IMessageBuilder
+ {
+ #region SongPositionPointerBuilder Members
+
+ #region Constants
+
+ // The number of ticks per 16th note.
+ private const int TicksPer16thNote = 6;
+
+ // Used for packing and unpacking the song position.
+ private const int Shift = 7;
+
+ // Used for packing and unpacking the song position.
+ private const int Mask = 127;
+
+ #endregion
+
+ #region Fields
+
+ // The scale used for converting from the song position to the position
+ // in ticks.
+ private int tickScale;
+
+ // Pulses per quarter note resolution.
+ private int ppqn;
+
+ // Used for building the SysCommonMessage to represent the song
+ // position pointer.
+ private SysCommonMessageBuilder builder;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the SongPositionPointerBuilder class.
+ ///
+ public SongPositionPointerBuilder()
+ {
+ builder = new SysCommonMessageBuilder();
+ builder.Type = SysCommonType.SongPositionPointer;
+
+ Ppqn = PpqnClock.PpqnMinValue;
+ }
+
+ ///
+ /// Initializes a new instance of the SongPositionPointerBuilder class
+ /// with the specified song position pointer message.
+ ///
+ ///
+ /// The song position pointer message to use for initializing the
+ /// SongPositionPointerBuilder.
+ ///
+ ///
+ /// If message is not a song position pointer message.
+ ///
+ public SongPositionPointerBuilder(SysCommonMessage message)
+ {
+ builder = new SysCommonMessageBuilder();
+ builder.Type = SysCommonType.SongPositionPointer;
+
+ Initialize(message);
+
+ Ppqn = PpqnClock.PpqnMinValue;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initializes the SongPositionPointerBuilder with the specified
+ /// SysCommonMessage.
+ ///
+ ///
+ /// The SysCommonMessage to use to initialize the
+ /// SongPositionPointerBuilder.
+ ///
+ ///
+ /// If the SysCommonMessage is not a song position pointer message.
+ ///
+ public void Initialize(SysCommonMessage message)
+ {
+ #region Require
+
+ if(message == null)
+ {
+ throw new ArgumentNullException("message");
+ }
+ else if(message.SysCommonType != SysCommonType.SongPositionPointer)
+ {
+ throw new ArgumentException(
+ "Message is not a song position pointer message.");
+ }
+
+ #endregion
+
+ builder.Initialize(message);
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets or sets the sequence position in ticks.
+ ///
+ ///
+ /// Value is set to less than zero.
+ ///
+ ///
+ /// Note: the position in ticks value is converted to the song position
+ /// pointer value. Since the song position pointer has a lower
+ /// resolution than the position in ticks, there is a probable loss of
+ /// resolution when setting the position in ticks value.
+ ///
+ public int PositionInTicks
+ {
+ get
+ {
+ return SongPosition * tickScale * TicksPer16thNote;
+ }
+ set
+ {
+ #region Require
+
+ if(value < 0)
+ {
+ throw new ArgumentOutOfRangeException("PositionInTicks", value,
+ "Position in ticks out of range.");
+ }
+
+ #endregion
+
+ SongPosition = value / (tickScale * TicksPer16thNote);
+ }
+ }
+
+ ///
+ /// Gets or sets the PulsesPerQuarterNote object.
+ ///
+ ///
+ /// Value is not a multiple of 24.
+ ///
+ public int Ppqn
+ {
+ get
+ {
+ return ppqn;
+ }
+ set
+ {
+ #region Require
+
+ if(value % PpqnClock.PpqnMinValue != 0)
+ {
+ throw new ArgumentException(
+ "Invalid pulses per quarter note value.");
+ }
+
+ #endregion
+
+ ppqn = value;
+
+ tickScale = ppqn / PpqnClock.PpqnMinValue;
+ }
+ }
+
+ ///
+ /// Gets or sets the song position.
+ ///
+ ///
+ /// Value is set to less than zero.
+ ///
+ public int SongPosition
+ {
+ get
+ {
+ return (builder.Data2 << Shift) | builder.Data1;
+ }
+ set
+ {
+ #region Require
+
+ if(value < 0)
+ {
+ throw new ArgumentOutOfRangeException("SongPosition", value,
+ "Song position pointer out of range.");
+ }
+
+ #endregion
+
+ builder.Data1 = value & Mask;
+ builder.Data2 = value >> Shift;
+ }
+ }
+
+ ///
+ /// Gets the built song position pointer message.
+ ///
+ public SysCommonMessage Result
+ {
+ get
+ {
+ return builder.Result;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IMessageBuilder Members
+
+ ///
+ /// Builds a song position pointer message.
+ ///
+ public void Build()
+ {
+ builder.Build();
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/SysCommonMessageBuilder.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/SysCommonMessageBuilder.cs
new file mode 100644
index 0000000..d79a1b9
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/SysCommonMessageBuilder.cs
@@ -0,0 +1,233 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Provides functionality for building SysCommonMessages.
+ ///
+ public class SysCommonMessageBuilder : IMessageBuilder
+ {
+ #region SysCommonMessageBuilder Members
+
+ #region Class Fields
+
+ // Stores the SystemCommonMessages.
+ private static Hashtable messageCache = Hashtable.Synchronized(new Hashtable());
+
+ #endregion
+
+ #region Fields
+
+ // The SystemCommonMessage as a packed integer.
+ private int message = 0;
+
+ // The built SystemCommonMessage.
+ private SysCommonMessage result = null;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the SysCommonMessageBuilder class.
+ ///
+ public SysCommonMessageBuilder()
+ {
+ Type = SysCommonType.TuneRequest;
+ }
+
+ ///
+ /// Initializes a new instance of the SysCommonMessageBuilder class
+ /// with the specified SystemCommonMessage.
+ ///
+ ///
+ /// The SysCommonMessage to use for initializing the
+ /// SysCommonMessageBuilder.
+ ///
+ ///
+ /// The SysCommonMessageBuilder uses the specified SysCommonMessage to
+ /// initialize its property values.
+ ///
+ public SysCommonMessageBuilder(SysCommonMessage message)
+ {
+ Initialize(message);
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initializes the SysCommonMessageBuilder with the specified
+ /// SysCommonMessage.
+ ///
+ ///
+ /// The SysCommonMessage to use for initializing the
+ /// SysCommonMessageBuilder.
+ ///
+ public void Initialize(SysCommonMessage message)
+ {
+ this.message = message.Message;
+ }
+
+ ///
+ /// Clears the SysCommonMessageBuilder cache.
+ ///
+ public static void Clear()
+ {
+ messageCache.Clear();
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the number of messages in the SysCommonMessageBuilder cache.
+ ///
+ public static int Count
+ {
+ get
+ {
+ return messageCache.Count;
+ }
+ }
+
+ ///
+ /// Gets the built SysCommonMessage.
+ ///
+ public SysCommonMessage Result
+ {
+ get
+ {
+ return result;
+ }
+ }
+
+ ///
+ /// Gets or sets the SysCommonMessage as a packed integer.
+ ///
+ internal int Message
+ {
+ get
+ {
+ return message;
+ }
+ set
+ {
+ message = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the type of SysCommonMessage.
+ ///
+ public SysCommonType Type
+ {
+ get
+ {
+ return (SysCommonType)ShortMessage.UnpackStatus(message);
+ }
+ set
+ {
+ message = ShortMessage.PackStatus(message, (int)value);
+ }
+ }
+
+ ///
+ /// Gets or sets the first data value to use for building the
+ /// SysCommonMessage.
+ ///
+ ///
+ /// Data1 is set to a value less than zero or greater than 127.
+ ///
+ public int Data1
+ {
+ get
+ {
+ return ShortMessage.UnpackData1(message);
+ }
+ set
+ {
+ message = ShortMessage.PackData1(message, value);
+ }
+ }
+
+ ///
+ /// Gets or sets the second data value to use for building the
+ /// SysCommonMessage.
+ ///
+ ///
+ /// Data2 is set to a value less than zero or greater than 127.
+ ///
+ public int Data2
+ {
+ get
+ {
+ return ShortMessage.UnpackData2(message);
+ }
+ set
+ {
+ message = ShortMessage.PackData2(message, value);
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IMessageBuilder Members
+
+ ///
+ /// Builds a SysCommonMessage.
+ ///
+ public void Build()
+ {
+ result = (SysCommonMessage)messageCache[message];
+
+ if(result == null)
+ {
+ result = new SysCommonMessage(message);
+
+ messageCache.Add(message, result);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/TempoChangeBuilder.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/TempoChangeBuilder.cs
new file mode 100644
index 0000000..0c0cbc6
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/TempoChangeBuilder.cs
@@ -0,0 +1,242 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Provides functionality for building tempo messages.
+ ///
+ public class TempoChangeBuilder : IMessageBuilder
+ {
+ #region TempoChangeBuilder Members
+
+ #region Constants
+
+ // Value used for shifting bits for packing and unpacking tempo values.
+ private const int Shift = 8;
+
+ #endregion
+
+ #region Fields
+
+ // The mesage's tempo.
+ private int tempo = PpqnClock.DefaultTempo;
+
+ // The built MetaMessage.
+ private MetaMessage result = null;
+
+ // Indicates whether the tempo property has been changed since
+ // the last time the message was built.
+ private bool changed = true;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the TempoChangeBuilder class.
+ ///
+ public TempoChangeBuilder()
+ {
+ }
+
+ ///
+ /// Initialize a new instance of the TempoChangeBuilder class with the
+ /// specified MetaMessage.
+ ///
+ ///
+ /// The MetaMessage to use for initializing the TempoChangeBuilder class.
+ ///
+ ///
+ /// If the specified MetaMessage is not a tempo type.
+ ///
+ ///
+ /// The TempoChangeBuilder uses the specified MetaMessage to initialize
+ /// its property values.
+ ///
+ public TempoChangeBuilder(MetaMessage e)
+ {
+ Initialize(e);
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initializes the TempoChangeBuilder with the specified MetaMessage.
+ ///
+ ///
+ /// The MetaMessage to use for initializing the TempoChangeBuilder.
+ ///
+ ///
+ /// If the specified MetaMessage is not a tempo type.
+ ///
+ public void Initialize(MetaMessage e)
+ {
+ #region Require
+
+ if(e == null)
+ {
+ throw new ArgumentNullException("e");
+ }
+ else if(e.MetaType != MetaType.Tempo)
+ {
+ throw new ArgumentException("Wrong meta message type.", "e");
+ }
+
+ #endregion
+
+ int t = 0;
+
+ // If this platform uses little endian byte order.
+ if(BitConverter.IsLittleEndian)
+ {
+ int d = e.Length - 1;
+
+ // Pack tempo.
+ for(int i = 0; i < e.Length; i++)
+ {
+ t |= e[d] << (Shift * i);
+ d--;
+ }
+ }
+ // Else this platform uses big endian byte order.
+ else
+ {
+ // Pack tempo.
+ for(int i = 0; i < e.Length; i++)
+ {
+ t |= e[i] << (Shift * i);
+ }
+ }
+
+ tempo = t;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets or sets the tempo.
+ ///
+ ///
+ /// Value is set to less than zero.
+ ///
+ public int Tempo
+ {
+ get
+ {
+ return tempo;
+ }
+ set
+ {
+ #region Require
+
+ if(value < 0)
+ {
+ throw new ArgumentOutOfRangeException("Tempo", value,
+ "Tempo is out of range.");
+ }
+
+ #endregion
+
+ tempo = value;
+
+ changed = true;
+ }
+ }
+
+ ///
+ /// Gets the built message.
+ ///
+ public MetaMessage Result
+ {
+ get
+ {
+ return result;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IMessageBuilder Members
+
+ ///
+ /// Builds the tempo change MetaMessage.
+ ///
+ public void Build()
+ {
+ // If the tempo has been changed since the last time the message
+ // was built.
+ if(changed)
+ {
+ byte[] data = new byte[MetaMessage.TempoLength];
+
+ // If this platform uses little endian byte order.
+ if(BitConverter.IsLittleEndian)
+ {
+ int d = data.Length - 1;
+
+ // Unpack tempo.
+ for(int i = 0; i < data.Length; i++)
+ {
+ data[d] = (byte)(tempo >> (Shift * i));
+ d--;
+ }
+ }
+ // Else this platform uses big endian byte order.
+ else
+ {
+ // Unpack tempo.
+ for(int i = 0; i < data.Length; i++)
+ {
+ data[i] = (byte)(tempo >> (Shift * i));
+ }
+ }
+
+ changed = false;
+
+ result = new MetaMessage(MetaType.Tempo, data);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/TimeSignatureBuilder.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/TimeSignatureBuilder.cs
new file mode 100644
index 0000000..90b17ec
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/Message Builders/TimeSignatureBuilder.cs
@@ -0,0 +1,277 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Provides easy to use functionality for time signature MetaMessages.
+ ///
+ public class TimeSignatureBuilder : IMessageBuilder
+ {
+ #region TimeSignature Members
+
+ #region Constants
+
+ // Default numerator value.
+ private const byte DefaultNumerator = 4;
+
+ // Default denominator value.
+ private const byte DefaultDenominator = 2;
+
+ // Default clocks per metronome click value.
+ private const byte DefaultClocksPerMetronomeClick = 24;
+
+ // Default thirty second notes per quarter note value.
+ private const byte DefaultThirtySecondNotesPerQuarterNote = 8;
+
+ #endregion
+
+ #region Fields
+
+ // The raw data making up the time signature meta message.
+ private byte[] data = new byte[MetaMessage.TimeSigLength];
+
+ // The built time signature meta message.
+ private MetaMessage result = null;
+
+ // Indicates whether any of the properties have changed since the
+ // last time the message was built.
+ private bool changed = true;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the TimeSignatureBuilder class.
+ ///
+ public TimeSignatureBuilder()
+ {
+ Numerator = DefaultNumerator;
+ Denominator = DefaultDenominator;
+ ClocksPerMetronomeClick = DefaultClocksPerMetronomeClick;
+ ThirtySecondNotesPerQuarterNote = DefaultThirtySecondNotesPerQuarterNote;
+ }
+
+ ///
+ /// Initializes a new instance of the TimeSignatureBuilder class with the
+ /// specified MetaMessage.
+ ///
+ ///
+ /// The MetaMessage to use for initializing the TimeSignatureBuilder class.
+ ///
+ ///
+ /// If the specified MetaMessage is not a time signature type.
+ ///
+ ///
+ /// The TimeSignatureBuilder uses the specified MetaMessage to
+ /// initialize its property values.
+ ///
+ public TimeSignatureBuilder(MetaMessage message)
+ {
+ Initialize(message);
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Initializes the TimeSignatureBuilder with the specified MetaMessage.
+ ///
+ ///
+ /// The MetaMessage to use for initializing the TimeSignatureBuilder.
+ ///
+ ///
+ /// If the specified MetaMessage is not a time signature type.
+ ///
+ public void Initialize(MetaMessage message)
+ {
+ #region Require
+
+ if(message.MetaType != MetaType.TimeSignature)
+ {
+ throw new ArgumentException("Wrong meta event type.", "message");
+ }
+
+ #endregion
+
+ data = message.GetBytes();
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets or sets the numerator.
+ ///
+ ///
+ /// Numerator is set to a value less than one.
+ ///
+ public byte Numerator
+ {
+ get
+ {
+ return data[0];
+ }
+ set
+ {
+ #region Require
+
+ if(value < 1)
+ {
+ throw new ArgumentOutOfRangeException("Numerator", value,
+ "Numerator out of range.");
+ }
+
+ #endregion
+
+ data[0] = value;
+
+ changed = true;
+ }
+ }
+
+ ///
+ /// Gets or sets the denominator.
+ ///
+ ///
+ /// Denominator is set to a value less than 2.
+ ///
+ ///
+ /// Denominator is set to a value that is not a power of 2.
+ ///
+ public byte Denominator
+ {
+ get
+ {
+ return Convert.ToByte(Math.Pow(2, data[1]));
+ }
+ set
+ {
+ #region Require
+
+ if(value < 2 || value > 32)
+ {
+ throw new ArgumentOutOfRangeException("Denominator must be between 2 and 32.");
+ }
+ else if((value & (value - 1)) != 0)
+ {
+ throw new ArgumentException("Denominator must be a power of 2.");
+ }
+
+ #endregion
+
+ data[1] = Convert.ToByte(Math.Log(value, 2));
+
+ changed = true;
+ }
+ }
+
+ ///
+ /// Gets or sets the clocks per metronome click.
+ ///
+ ///
+ /// Clocks per metronome click determines how many MIDI clocks occur
+ /// for each metronome click.
+ ///
+ public byte ClocksPerMetronomeClick
+ {
+ get
+ {
+ return data[2];
+ }
+ set
+ {
+ data[2] = value;
+
+ changed = true;
+ }
+ }
+
+ ///
+ /// Gets or sets how many thirty second notes there are for each
+ /// quarter note.
+ ///
+ public byte ThirtySecondNotesPerQuarterNote
+ {
+ get
+ {
+ return data[3];
+ }
+ set
+ {
+ data[3] = value;
+
+ changed = true;
+ }
+ }
+
+ ///
+ /// Gets the built message.
+ ///
+ public MetaMessage Result
+ {
+ get
+ {
+ return result;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IMessageBuilder Members
+
+ ///
+ /// Builds the time signature MetaMessage.
+ ///
+ public void Build()
+ {
+ // If any of the properties have changed since the last time the
+ // message was built.
+ if(changed)
+ {
+ result = new MetaMessage(MetaType.TimeSignature, data);
+ changed = false;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/MessageDispatcher.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/MessageDispatcher.cs
new file mode 100644
index 0000000..ef5d800
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/MessageDispatcher.cs
@@ -0,0 +1,184 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Dispatches IMidiMessages to their corresponding sink.
+ ///
+ public class MessageDispatcher
+ {
+ #region MessageDispatcher Members
+
+ #region Events
+
+ public event EventHandler ChannelMessageDispatched;
+
+ public event EventHandler SysExMessageDispatched;
+
+ public event EventHandler SysCommonMessageDispatched;
+
+ public event EventHandler SysRealtimeMessageDispatched;
+
+ public event EventHandler MetaMessageDispatched;
+
+ #endregion
+
+ ///
+ /// Dispatches IMidiMessages to their corresponding sink.
+ ///
+ ///
+ /// The IMidiMessage to dispatch.
+ ///
+ public void Dispatch(IMidiMessage message)
+ {
+ #region Require
+
+ if(message == null)
+ {
+ throw new ArgumentNullException("message");
+ }
+
+ #endregion
+
+ switch(message.MessageType)
+ {
+ case MessageType.Channel:
+ OnChannelMessageDispatched(new ChannelMessageEventArgs((ChannelMessage)message));
+ break;
+
+ case MessageType.SystemExclusive:
+ OnSysExMessageDispatched(new SysExMessageEventArgs((SysExMessage)message));
+ break;
+
+ case MessageType.Meta:
+ OnMetaMessageDispatched(new MetaMessageEventArgs((MetaMessage)message));
+ break;
+
+ case MessageType.SystemCommon:
+ OnSysCommonMessageDispatched(new SysCommonMessageEventArgs((SysCommonMessage)message));
+ break;
+
+ case MessageType.SystemRealtime:
+ switch(((SysRealtimeMessage)message).SysRealtimeType)
+ {
+ case SysRealtimeType.ActiveSense:
+ OnSysRealtimeMessageDispatched(SysRealtimeMessageEventArgs.ActiveSense);
+ break;
+
+ case SysRealtimeType.Clock:
+ OnSysRealtimeMessageDispatched(SysRealtimeMessageEventArgs.Clock);
+ break;
+
+ case SysRealtimeType.Continue:
+ OnSysRealtimeMessageDispatched(SysRealtimeMessageEventArgs.Continue);
+ break;
+
+ case SysRealtimeType.Reset:
+ OnSysRealtimeMessageDispatched(SysRealtimeMessageEventArgs.Reset);
+ break;
+
+ case SysRealtimeType.Start:
+ OnSysRealtimeMessageDispatched(SysRealtimeMessageEventArgs.Start);
+ break;
+
+ case SysRealtimeType.Stop:
+ OnSysRealtimeMessageDispatched(SysRealtimeMessageEventArgs.Stop);
+ break;
+
+ case SysRealtimeType.Tick:
+ OnSysRealtimeMessageDispatched(SysRealtimeMessageEventArgs.Tick);
+ break;
+ }
+
+ break;
+ }
+ }
+
+ protected virtual void OnChannelMessageDispatched(ChannelMessageEventArgs e)
+ {
+ EventHandler handler = ChannelMessageDispatched;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ protected virtual void OnSysExMessageDispatched(SysExMessageEventArgs e)
+ {
+ EventHandler handler = SysExMessageDispatched;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ protected virtual void OnSysCommonMessageDispatched(SysCommonMessageEventArgs e)
+ {
+ EventHandler handler = SysCommonMessageDispatched;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ protected virtual void OnSysRealtimeMessageDispatched(SysRealtimeMessageEventArgs e)
+ {
+ EventHandler handler = SysRealtimeMessageDispatched;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ protected virtual void OnMetaMessageDispatched(MetaMessageEventArgs e)
+ {
+ EventHandler handler = MetaMessageDispatched;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/MetaMessage.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/MetaMessage.cs
new file mode 100644
index 0000000..3b4478f
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/MetaMessage.cs
@@ -0,0 +1,513 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+
+namespace Sanford.Multimedia.Midi
+{
+ #region Meta Message Types
+
+ ///
+ /// Represents MetaMessage types.
+ ///
+ public enum MetaType
+ {
+ ///
+ /// Represents sequencer number type.
+ ///
+ SequenceNumber,
+
+ ///
+ /// Represents the text type.
+ ///
+ Text,
+
+ ///
+ /// Represents the copyright type.
+ ///
+ Copyright,
+
+ ///
+ /// Represents the track name type.
+ ///
+ TrackName,
+
+ ///
+ /// Represents the instrument name type.
+ ///
+ InstrumentName,
+
+ ///
+ /// Represents the lyric type.
+ ///
+ Lyric,
+
+ ///
+ /// Represents the marker type.
+ ///
+ Marker,
+
+ ///
+ /// Represents the cue point type.
+ ///
+ CuePoint,
+
+ ///
+ /// Represents the program name type.
+ ///
+ ProgramName,
+
+ ///
+ /// Represents the device name type.
+ ///
+ DeviceName,
+
+ ///
+ /// Represents then end of track type.
+ ///
+ EndOfTrack = 0x2F,
+
+ ///
+ /// Represents the tempo type.
+ ///
+ Tempo = 0x51,
+
+ ///
+ /// Represents the Smpte offset type.
+ ///
+ SmpteOffset = 0x54,
+
+ ///
+ /// Represents the time signature type.
+ ///
+ TimeSignature = 0x58,
+
+ ///
+ /// Represents the key signature type.
+ ///
+ KeySignature,
+
+ ///
+ /// Represents the proprietary event type.
+ ///
+ ProprietaryEvent = 0x7F
+ }
+
+ #endregion
+
+ ///
+ /// Represents MIDI meta messages.
+ ///
+ ///
+ /// Meta messages are MIDI messages that are stored in MIDI files. These
+ /// messages are not sent or received via MIDI but are read and
+ /// interpretted from MIDI files. They provide information that describes
+ /// a MIDI file's properties. For example, tempo changes are implemented
+ /// using meta messages.
+ ///
+ [ImmutableObject(true)]
+ public sealed class MetaMessage : IMidiMessage
+ {
+ #region MetaMessage Members
+
+ #region Constants
+
+ ///
+ /// The amount to shift data bytes when calculating the hash code.
+ ///
+ private const int Shift = 7;
+
+ //
+ // Meta message length constants.
+ //
+
+ ///
+ /// Length in bytes for tempo meta message data.
+ ///
+ public const int TempoLength = 3;
+
+ ///
+ /// Length in bytes for SMPTE offset meta message data.
+ ///
+ public const int SmpteOffsetLength = 5;
+
+ ///
+ /// Length in bytes for time signature meta message data.
+ ///
+ public const int TimeSigLength = 4;
+
+ ///
+ /// Length in bytes for key signature meta message data.
+ ///
+ public const int KeySigLength = 2;
+
+ #endregion
+
+ #region Class Fields
+
+ ///
+ /// End of track meta message.
+ ///
+ public static readonly MetaMessage EndOfTrackMessage =
+ new MetaMessage(MetaType.EndOfTrack, new byte[0]);
+
+ #endregion
+
+ #region Fields
+
+ // The meta message type.
+ private MetaType type;
+
+ // The meta message data.
+ private byte[] data;
+
+ // The hash code value.
+ private int hashCode;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the MetaMessage class.
+ ///
+ ///
+ /// The type of MetaMessage.
+ ///
+ ///
+ /// The MetaMessage data.
+ ///
+ ///
+ /// The length of the MetaMessage is not valid for the MetaMessage type.
+ ///
+ ///
+ /// Each MetaMessage has type and length properties. For certain
+ /// types, the length of the message data must be a specific value. For
+ /// example, tempo messages must have a data length of exactly three.
+ /// Some MetaMessage types can have any data length. Text messages are
+ /// an example of a MetaMessage that can have a variable data length.
+ /// When a MetaMessage is created, the length of the data is checked
+ /// to make sure that it is valid for the specified type. If it is not,
+ /// an exception is thrown.
+ ///
+ public MetaMessage(MetaType type, byte[] data)
+ {
+ #region Require
+
+ if(data == null)
+ {
+ throw new ArgumentNullException("data");
+ }
+ else if(!ValidateDataLength(type, data.Length))
+ {
+ throw new ArgumentException(
+ "Length of data not valid for meta message type.");
+ }
+
+ #endregion
+
+ this.type = type;
+
+ // Create storage for meta message data.
+ this.data = new byte[data.Length];
+
+ // Copy data into storage.
+ data.CopyTo(this.data, 0);
+
+ CalculateHashCode();
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Gets a copy of the data bytes for this meta message.
+ ///
+ ///
+ /// A copy of the data bytes for this meta message.
+ ///
+ public byte[] GetBytes()
+ {
+ return (byte[])data.Clone();
+ }
+
+ ///
+ /// Returns a value for the current MetaMessage suitable for use in
+ /// hashing algorithms.
+ ///
+ ///
+ /// A hash code for the current MetaMessage.
+ ///
+ public override int GetHashCode()
+ {
+ return hashCode;
+ }
+
+ ///
+ /// Determines whether two MetaMessage instances are equal.
+ ///
+ ///
+ /// The MetaMessage to compare with the current MetaMessage.
+ ///
+ ///
+ /// true if the specified MetaMessage is equal to the current
+ /// MetaMessage; otherwise, false.
+ ///
+ public override bool Equals(object obj)
+ {
+ #region Guard
+
+ if(!(obj is MetaMessage))
+ {
+ return false;
+ }
+
+ #endregion
+
+ bool equal = true;
+ MetaMessage message = (MetaMessage)obj;
+
+ // If the types do not match.
+ if(MetaType != message.MetaType)
+ {
+ // The messages are not equal
+ equal = false;
+ }
+
+ // If the message lengths are not equal.
+ if(equal && Length != message.Length)
+ {
+ // The message are not equal.
+ equal = false;
+ }
+
+ // Check to see if the data is equal.
+ for(int i = 0; i < Length && equal; i++)
+ {
+ // If a data value does not match.
+ if(this[i] != message[i])
+ {
+ // The messages are not equal.
+ equal = false;
+ }
+ }
+
+ return equal;
+ }
+
+ // Calculates the hash code.
+ private void CalculateHashCode()
+ {
+ // TODO: This algorithm may need work.
+
+ hashCode = (int)MetaType;
+
+ for(int i = 0; i < data.Length; i += 3)
+ {
+ hashCode ^= data[i];
+ }
+
+ for(int i = 1; i < data.Length; i += 3)
+ {
+ hashCode ^= data[i] << Shift;
+ }
+
+ for(int i = 2; i < data.Length; i += 3)
+ {
+ hashCode ^= data[i] << Shift * 2;
+ }
+ }
+
+ ///
+ /// Validates data length.
+ ///
+ ///
+ /// The MetaMessage type.
+ ///
+ ///
+ /// The length of the MetaMessage data.
+ ///
+ ///
+ /// true if the data length is valid for this type of
+ /// MetaMessage; otherwise, false.
+ ///
+ private bool ValidateDataLength(MetaType type, int length)
+ {
+ #region Require
+
+ Debug.Assert(length >= 0);
+
+ #endregion
+
+ bool result = true;
+
+ // Determine which type of meta message this is and check to make
+ // sure that the data length value is valid.
+ switch(type)
+ {
+ case MetaType.SequenceNumber:
+ if(length != 0 || length != 2)
+ {
+ result = false;
+ }
+ break;
+
+ case MetaType.EndOfTrack:
+ if(length != 0)
+ {
+ result = false;
+ }
+ break;
+
+ case MetaType.Tempo:
+ if(length != TempoLength)
+ {
+ result = false;
+ }
+ break;
+
+ case MetaType.SmpteOffset:
+ if(length != SmpteOffsetLength)
+ {
+ result = false;
+ }
+ break;
+
+ case MetaType.TimeSignature:
+ if(length != TimeSigLength)
+ {
+ result = false;
+ }
+ break;
+
+ case MetaType.KeySignature:
+ if(length != KeySigLength)
+ {
+ result = false;
+ }
+ break;
+
+ default:
+ result = true;
+ break;
+ }
+
+ return result;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the element at the specified index.
+ ///
+ ///
+ /// index is less than zero or greater than or equal to Length.
+ ///
+ public byte this[int index]
+ {
+ get
+ {
+ #region Require
+
+ if(index < 0 || index >= Length)
+ {
+ throw new ArgumentOutOfRangeException("index", index,
+ "Index into MetaMessage out of range.");
+ }
+
+ #endregion
+
+ return data[index];
+ }
+ }
+
+ ///
+ /// Gets the length of the meta message.
+ ///
+ public int Length
+ {
+ get
+ {
+ return data.Length;
+ }
+ }
+
+ ///
+ /// Gets the type of meta message.
+ ///
+ public MetaType MetaType
+ {
+ get
+ {
+ return type;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #region IMidiMessage Members
+
+ ///
+ /// Gets the status value.
+ ///
+ public int Status
+ {
+ get
+ {
+ // All meta messages have the same status value (0xFF).
+ return 0xFF;
+ }
+ }
+
+ ///
+ /// Gets the MetaMessage's MessageType.
+ ///
+ public MessageType MessageType
+ {
+ get
+ {
+ return MessageType.Meta;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/ShortMessage.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/ShortMessage.cs
new file mode 100644
index 0000000..f7d58af
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/ShortMessage.cs
@@ -0,0 +1,179 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Represents the basic class for all MIDI short messages.
+ ///
+ ///
+ /// MIDI short messages represent all MIDI messages except meta messages
+ /// and system exclusive messages. This includes channel messages, system
+ /// realtime messages, and system common messages.
+ ///
+ public abstract class ShortMessage : IMidiMessage
+ {
+ #region ShortMessage Members
+
+ #region Constants
+
+ public const int DataMaxValue= 127;
+
+ public const int StatusMaxValue = 255;
+
+ //
+ // Bit manipulation constants.
+ //
+
+ private const int StatusMask = ~255;
+ protected const int DataMask = ~StatusMask;
+ private const int Data1Mask = ~65280;
+ private const int Data2Mask = ~Data1Mask + DataMask;
+ private const int Shift = 8;
+
+ #endregion
+
+ protected int msg = 0;
+
+ #region Methods
+
+ public byte[] GetBytes()
+ {
+ return BitConverter.GetBytes(msg);
+ }
+
+ internal static int PackStatus(int message, int status)
+ {
+ #region Require
+
+ if(status < 0 || status > StatusMaxValue)
+ {
+ throw new ArgumentOutOfRangeException("status", status,
+ "Status value out of range.");
+ }
+
+ #endregion
+
+ return (message & StatusMask) | status;
+ }
+
+ internal static int PackData1(int message, int data1)
+ {
+ #region Require
+
+ if(data1 < 0 || data1 > DataMaxValue)
+ {
+ throw new ArgumentOutOfRangeException("data1", data1,
+ "Data 1 value out of range.");
+ }
+
+ #endregion
+
+ return (message & Data1Mask) | (data1 << Shift);
+ }
+
+ internal static int PackData2(int message, int data2)
+ {
+ #region Require
+
+ if(data2 < 0 || data2 > DataMaxValue)
+ {
+ throw new ArgumentOutOfRangeException("data2", data2,
+ "Data 2 value out of range.");
+ }
+
+ #endregion
+
+ return (message & Data2Mask) | (data2 << (Shift * 2));
+ }
+
+ internal static int UnpackStatus(int message)
+ {
+ return message & DataMask;
+ }
+
+ internal static int UnpackData1(int message)
+ {
+ return (message & ~Data1Mask) >> Shift;
+ }
+
+ internal static int UnpackData2(int message)
+ {
+ return (message & ~Data2Mask) >> (Shift * 2);
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the short message as a packed integer.
+ ///
+ ///
+ /// The message is packed into an integer value with the low-order byte
+ /// of the low-word representing the status value. The high-order byte
+ /// of the low-word represents the first data value, and the low-order
+ /// byte of the high-word represents the second data value.
+ ///
+ public int Message
+ {
+ get
+ {
+ return msg;
+ }
+ }
+
+ ///
+ /// Gets the messages's status value.
+ ///
+ public int Status
+ {
+ get
+ {
+ return UnpackStatus(msg);
+ }
+ }
+
+ public abstract MessageType MessageType
+ {
+ get;
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/SysCommonMessage.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/SysCommonMessage.cs
new file mode 100644
index 0000000..767391c
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/SysCommonMessage.cs
@@ -0,0 +1,257 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+
+namespace Sanford.Multimedia.Midi
+{
+ #region System Common Message Types
+
+ ///
+ /// Defines constants representing the various system common message types.
+ ///
+ public enum SysCommonType
+ {
+ ///
+ /// Represents the MTC system common message type.
+ ///
+ MidiTimeCode = 0xF1,
+
+ ///
+ /// Represents the song position pointer type.
+ ///
+ SongPositionPointer,
+
+ ///
+ /// Represents the song select type.
+ ///
+ SongSelect,
+
+ ///
+ /// Represents the tune request type.
+ ///
+ TuneRequest = 0xF6
+ }
+
+ #endregion
+
+ ///
+ /// Represents MIDI system common messages.
+ ///
+ [ImmutableObject(true)]
+ public sealed class SysCommonMessage : ShortMessage
+ {
+ #region SysCommonMessage Members
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the SysCommonMessage class with the
+ /// specified type.
+ ///
+ ///
+ /// The type of SysCommonMessage.
+ ///
+ public SysCommonMessage(SysCommonType type)
+ {
+ msg = (int)type;
+
+ #region Ensure
+
+ Debug.Assert(SysCommonType == type);
+
+ #endregion
+ }
+
+ ///
+ /// Initializes a new instance of the SysCommonMessage class with the
+ /// specified type and the first data value.
+ ///
+ ///
+ /// The type of SysCommonMessage.
+ ///
+ ///
+ /// The first data value.
+ ///
+ ///
+ /// If data1 is less than zero or greater than 127.
+ ///
+ public SysCommonMessage(SysCommonType type, int data1)
+ {
+ msg = (int)type;
+ msg = PackData1(msg, data1);
+
+ #region Ensure
+
+ Debug.Assert(SysCommonType == type);
+ Debug.Assert(Data1 == data1);
+
+ #endregion
+ }
+
+ ///
+ /// Initializes a new instance of the SysCommonMessage class with the
+ /// specified type, first data value, and second data value.
+ ///
+ ///
+ /// The type of SysCommonMessage.
+ ///
+ ///
+ /// The first data value.
+ ///
+ ///
+ /// The second data value.
+ ///
+ ///
+ /// If data1 or data2 is less than zero or greater than 127.
+ ///
+ public SysCommonMessage(SysCommonType type, int data1, int data2)
+ {
+ msg = (int)type;
+ msg = PackData1(msg, data1);
+ msg = PackData2(msg, data2);
+
+ #region Ensure
+
+ Debug.Assert(SysCommonType == type);
+ Debug.Assert(Data1 == data1);
+ Debug.Assert(Data2 == data2);
+
+ #endregion
+ }
+
+ internal SysCommonMessage(int message)
+ {
+ this.msg = message;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Returns a value for the current SysCommonMessage suitable for use
+ /// in hashing algorithms.
+ ///
+ ///
+ /// A hash code for the current SysCommonMessage.
+ ///
+ public override int GetHashCode()
+ {
+ return msg;
+ }
+
+ ///
+ /// Determines whether two SysCommonMessage instances are equal.
+ ///
+ ///
+ /// The SysCommonMessage to compare with the current SysCommonMessage.
+ ///
+ ///
+ /// true if the specified SysCommonMessage is equal to the
+ /// current SysCommonMessage; otherwise, false.
+ ///
+ public override bool Equals(object obj)
+ {
+ #region Guard
+
+ if(!(obj is SysCommonMessage))
+ {
+ return false;
+ }
+
+ #endregion
+
+ SysCommonMessage message = (SysCommonMessage)obj;
+
+ return (this.SysCommonType == message.SysCommonType &&
+ this.Data1 == message.Data1 &&
+ this.Data2 == message.Data2);
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the SysCommonType.
+ ///
+ public SysCommonType SysCommonType
+ {
+ get
+ {
+ return (SysCommonType)UnpackStatus(msg);
+ }
+ }
+
+ ///
+ /// Gets the first data value.
+ ///
+ public int Data1
+ {
+ get
+ {
+ return UnpackData1(msg);
+ }
+ }
+
+ ///
+ /// Gets the second data value.
+ ///
+ public int Data2
+ {
+ get
+ {
+ return UnpackData2(msg);
+ }
+ }
+
+ ///
+ /// Gets the MessageType.
+ ///
+ public override MessageType MessageType
+ {
+ get
+ {
+ return MessageType.SystemCommon;
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/SysExMessage.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/SysExMessage.cs
new file mode 100644
index 0000000..7ac8b16
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/SysExMessage.cs
@@ -0,0 +1,254 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Defines constants representing various system exclusive message types.
+ ///
+ public enum SysExType
+ {
+ ///
+ /// Represents the start of system exclusive message type.
+ ///
+ Start = 0xF0,
+
+ ///
+ /// Represents the continuation of a system exclusive message.
+ ///
+ Continuation = 0xF7
+ }
+
+ ///
+ /// Represents MIDI system exclusive messages.
+ ///
+ public sealed class SysExMessage : IMidiMessage, IEnumerable
+ {
+ #region SysExEventMessage Members
+
+ #region Constants
+
+ ///
+ /// Maximum value for system exclusive channels.
+ ///
+ public const int SysExChannelMaxValue = 127;
+
+ #endregion
+
+ #region Fields
+
+ // The system exclusive data.
+ private byte[] data;
+
+ #endregion
+
+ #region Construction
+
+ ///
+ /// Initializes a new instance of the SysExMessageEventArgs class with the
+ /// specified system exclusive data.
+ ///
+ ///
+ /// The system exclusive data.
+ ///
+ ///
+ /// The system exclusive data's status byte, the first byte in the
+ /// data, must have a value of 0xF0 or 0xF7.
+ ///
+ public SysExMessage(byte[] data)
+ {
+ #region Require
+
+ if(data.Length < 1)
+ {
+ throw new ArgumentException(
+ "System exclusive data is too short.", "data");
+ }
+ else if(data[0] != (byte)SysExType.Start &&
+ data[0] != (byte)SysExType.Continuation)
+ {
+ throw new ArgumentException(
+ "Unknown status value.", "data");
+ }
+
+ #endregion
+
+ this.data = new byte[data.Length];
+ data.CopyTo(this.data, 0);
+ }
+
+ #endregion
+
+ #region Methods
+
+ public byte[] GetBytes()
+ {
+ byte[] clone = new byte[data.Length];
+
+ data.CopyTo(clone, 0);
+
+ return clone;
+ }
+
+ public void CopyTo(byte[] buffer, int index)
+ {
+ data.CopyTo(buffer, index);
+ }
+
+ public override bool Equals(object obj)
+ {
+ #region Guard
+
+ if(!(obj is SysExMessage))
+ {
+ return false;
+ }
+
+ #endregion
+
+ SysExMessage message = (SysExMessage)obj;
+
+ bool equals = true;
+
+ if(this.Length != message.Length)
+ {
+ equals = false;
+ }
+
+ for(int i = 0; i < this.Length && equals; i++)
+ {
+ if(this[i] != message[i])
+ {
+ equals = false;
+ }
+ }
+
+ return equals;
+ }
+
+ public override int GetHashCode()
+ {
+ return data.GetHashCode();
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the element at the specified index.
+ ///
+ ///
+ /// If index is less than zero or greater than or equal to the length
+ /// of the message.
+ ///
+ public byte this[int index]
+ {
+ get
+ {
+ #region Require
+
+ if(index < 0 || index >= Length)
+ {
+ throw new ArgumentOutOfRangeException("index", index,
+ "Index into system exclusive message out of range.");
+ }
+
+ #endregion
+
+ return data[index];
+ }
+ }
+
+ ///
+ /// Gets the length of the system exclusive data.
+ ///
+ public int Length
+ {
+ get
+ {
+ return data.Length;
+ }
+ }
+
+ ///
+ /// Gets the system exclusive type.
+ ///
+ public SysExType SysExType
+ {
+ get
+ {
+ return (SysExType)data[0];
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ ///
+ /// Gets the status value.
+ ///
+ public int Status
+ {
+ get
+ {
+ return (int)data[0];
+ }
+ }
+
+ ///
+ /// Gets the MessageType.
+ ///
+ public MessageType MessageType
+ {
+ get
+ {
+ return MessageType.SystemExclusive;
+ }
+ }
+
+ #region IEnumerable Members
+
+ public IEnumerator GetEnumerator()
+ {
+ return data.GetEnumerator();
+ }
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Messages/SysRealtimeMessage.cs b/DependenciesCode/Sanford.Multimedia.Midi/Messages/SysRealtimeMessage.cs
new file mode 100644
index 0000000..9544af6
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Messages/SysRealtimeMessage.cs
@@ -0,0 +1,227 @@
+#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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+
+namespace Sanford.Multimedia.Midi
+{
+ #region System Realtime Message Types
+
+ ///
+ /// Defines constants representing the various system realtime message types.
+ ///
+ public enum SysRealtimeType
+ {
+ ///
+ /// Represents the clock system realtime type.
+ ///
+ Clock = 0xF8,
+
+ ///
+ /// Represents the tick system realtime type.
+ ///
+ Tick,
+
+ ///
+ /// Represents the start system realtime type.
+ ///
+ Start,
+
+ ///
+ /// Represents the continue system realtime type.
+ ///
+ Continue,
+
+ ///
+ /// Represents the stop system realtime type.
+ ///
+ Stop,
+
+ ///
+ /// Represents the active sense system realtime type.
+ ///
+ ActiveSense = 0xFE,
+
+ ///
+ /// Represents the reset system realtime type.
+ ///
+ Reset
+ }
+
+ #endregion
+
+ ///
+ /// Represents MIDI system realtime messages.
+ ///
+ ///
+ /// System realtime messages are MIDI messages that are primarily concerned
+ /// with controlling and synchronizing MIDI devices.
+ ///
+ [ImmutableObject(true)]
+ public sealed class SysRealtimeMessage : ShortMessage
+ {
+ #region SysRealtimeMessage Members
+
+ #region System Realtime Messages
+
+ ///
+ /// The instance of the system realtime start message.
+ ///
+ public static readonly SysRealtimeMessage StartMessage =
+ new SysRealtimeMessage(SysRealtimeType.Start);
+
+ ///
+ /// The instance of the system realtime continue message.
+ ///
+ public static readonly SysRealtimeMessage ContinueMessage =
+ new SysRealtimeMessage(SysRealtimeType.Continue);
+
+ ///
+ /// The instance of the system realtime stop message.
+ ///
+ public static readonly SysRealtimeMessage StopMessage =
+ new SysRealtimeMessage(SysRealtimeType.Stop);
+
+ ///
+ /// The instance of the system realtime clock message.
+ ///
+ public static readonly SysRealtimeMessage ClockMessage =
+ new SysRealtimeMessage(SysRealtimeType.Clock);
+
+ ///
+ /// The instance of the system realtime tick message.
+ ///
+ public static readonly SysRealtimeMessage TickMessage =
+ new SysRealtimeMessage(SysRealtimeType.Tick);
+
+ ///
+ /// The instance of the system realtime active sense message.
+ ///
+ public static readonly SysRealtimeMessage ActiveSenseMessage =
+ new SysRealtimeMessage(SysRealtimeType.ActiveSense);
+
+ ///
+ /// The instance of the system realtime reset message.
+ ///
+ public static readonly SysRealtimeMessage ResetMessage =
+ new SysRealtimeMessage(SysRealtimeType.Reset);
+
+ #endregion
+
+ // Make construction private so that a system realtime message cannot
+ // be constructed directly.
+ private SysRealtimeMessage(SysRealtimeType type)
+ {
+ msg = (int)type;
+
+ #region Ensure
+
+ Debug.Assert(SysRealtimeType == type);
+
+ #endregion
+ }
+
+ #region Methods
+
+ ///
+ /// Returns a value for the current SysRealtimeMessage suitable for use in
+ /// hashing algorithms.
+ ///
+ ///
+ /// A hash code for the current SysRealtimeMessage.
+ ///
+ public override int GetHashCode()
+ {
+ return msg;
+ }
+
+ ///
+ /// Determines whether two SysRealtimeMessage instances are equal.
+ ///
+ ///
+ /// The SysRealtimeMessage to compare with the current SysRealtimeMessage.
+ ///
+ ///
+ /// true if the specified SysRealtimeMessage is equal to the current
+ /// SysRealtimeMessage; otherwise, false.
+ ///
+ public override bool Equals(object obj)
+ {
+ #region Guard
+
+ if(!(obj is SysRealtimeMessage))
+ {
+ return false;
+ }
+
+ #endregion
+
+ SysRealtimeMessage message = (SysRealtimeMessage)obj;
+
+ return this.msg == message.msg;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the SysRealtimeType.
+ ///
+ public SysRealtimeType SysRealtimeType
+ {
+ get
+ {
+ return (SysRealtimeType)msg;
+ }
+ }
+
+ ///
+ /// Gets the MessageType.
+ ///
+ public override MessageType MessageType
+ {
+ get
+ {
+ return MessageType.SystemRealtime;
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/MidiNoteConverter.cs b/DependenciesCode/Sanford.Multimedia.Midi/MidiNoteConverter.cs
new file mode 100644
index 0000000..942b54e
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/MidiNoteConverter.cs
@@ -0,0 +1,156 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Converts a MIDI note number to its corresponding frequency.
+ ///
+ public sealed class MidiNoteConverter
+ {
+ ///
+ /// The minimum value a note ID can have.
+ ///
+ public const int NoteIDMinValue = 0;
+
+ ///
+ /// The maximum value a note ID can have.
+ ///
+ public const int NoteIDMaxValue = 127;
+
+ // Table for holding frequency values.
+ private readonly static double[] NoteToFrequencyTable = new double[NoteIDMaxValue + 1];
+
+ static MidiNoteConverter()
+ {
+ // The number of notes per octave.
+ int notesPerOctave = 12;
+
+ // Reference frequency used for calculations.
+ double referenceFrequency = 440;
+
+ // The note ID of the reference frequency.
+ int referenceNoteID = 69;
+
+ double exponent;
+
+ // Fill table with the frequencies of all MIDI notes.
+ for(int i = 0; i < NoteToFrequencyTable.Length; i++)
+ {
+ exponent = (double)(i - referenceNoteID) / notesPerOctave;
+
+ NoteToFrequencyTable[i] = referenceFrequency * Math.Pow(2.0, exponent);
+ }
+ }
+
+ // Prevents instances of this class from being created - no need for
+ // an instance to be created since this class only has static methods.
+ private MidiNoteConverter()
+ {
+ }
+
+ ///
+ /// Converts the specified note to a frequency.
+ ///
+ ///
+ /// The ID of the note to convert.
+ ///
+ ///
+ /// The frequency of the specified note.
+ ///
+ public static double NoteToFrequency(int noteID)
+ {
+ #region Require
+
+ if(noteID < NoteIDMinValue || noteID > NoteIDMaxValue)
+ {
+ throw new ArgumentOutOfRangeException("Note ID out of range.");
+ }
+
+ #endregion
+
+ return NoteToFrequencyTable[noteID];
+ }
+
+ ///
+ /// Converts the specified frequency to a note.
+ ///
+ ///
+ /// The frequency to convert.
+ ///
+ ///
+ /// The ID of the note closest to the specified frequency.
+ ///
+ public static int FrequencyToNote(double frequency)
+ {
+ int noteID = 0;
+ bool found = false;
+
+ // Search for the note with a frequency near the specified frequency.
+ for(int i = 0; i < NoteIDMaxValue && !found; i++)
+ {
+ noteID = i;
+
+ // If the specified frequency is less than the frequency of
+ // the next note.
+ if(frequency < NoteToFrequency(noteID + 1))
+ {
+ // Indicate that the note ID for the specified frequency
+ // has been found.
+ found = true;
+ }
+ }
+
+ // If the note is not the first or last note, narrow the results.
+ if(noteID > 0 && noteID < NoteIDMaxValue)
+ {
+ // Get the frequency of the previous note.
+ double previousFrequncy = NoteToFrequency(noteID - 1);
+ // Get the frequency of the next note.
+ double nextFrequency = NoteToFrequency(noteID + 1);
+
+ // If the next note is closer in frequency than the previous note.
+ if(nextFrequency - frequency < frequency - previousFrequncy)
+ {
+ // Move to the next note.
+ noteID++;
+ }
+ }
+
+ return noteID;
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Processing/ChannelChaser.cs b/DependenciesCode/Sanford.Multimedia.Midi/Processing/ChannelChaser.cs
new file mode 100644
index 0000000..feca5a6
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Processing/ChannelChaser.cs
@@ -0,0 +1,167 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class ChannelChaser
+ {
+ private ChannelMessage[,] controllerMessages;
+
+ private ChannelMessage[] programChangeMessages;
+
+ private ChannelMessage[] pitchBendMessages;
+
+ private ChannelMessage[] channelPressureMessages;
+
+ private ChannelMessage[] polyPressureMessages;
+
+ public event EventHandler Chased;
+
+ public ChannelChaser()
+ {
+ int c = ChannelMessage.MidiChannelMaxValue + 1;
+ int d = ShortMessage.DataMaxValue + 1;
+
+ controllerMessages = new ChannelMessage[c, d];
+
+ programChangeMessages = new ChannelMessage[c];
+ pitchBendMessages = new ChannelMessage[c];
+ channelPressureMessages = new ChannelMessage[c];
+ polyPressureMessages = new ChannelMessage[c];
+ }
+
+ public void Process(ChannelMessage message)
+ {
+ switch(message.Command)
+ {
+ case ChannelCommand.Controller:
+ controllerMessages[message.MidiChannel, message.Data1] = message;
+ break;
+
+ case ChannelCommand.ChannelPressure:
+ channelPressureMessages[message.MidiChannel] = message;
+ break;
+
+ case ChannelCommand.PitchWheel:
+ pitchBendMessages[message.MidiChannel] = message;
+ break;
+
+ case ChannelCommand.PolyPressure:
+ polyPressureMessages[message.MidiChannel] = message;
+ break;
+
+ case ChannelCommand.ProgramChange:
+ programChangeMessages[message.MidiChannel] = message;
+ break;
+ }
+ }
+
+ public void Chase()
+ {
+ ArrayList chasedMessages = new ArrayList();
+
+ for(int c = 0; c <= ChannelMessage.MidiChannelMaxValue; c++)
+ {
+ for(int n = 0; n <= ShortMessage.DataMaxValue; n++)
+ {
+ if(controllerMessages[c, n] != null)
+ {
+ chasedMessages.Add(controllerMessages[c, n]);
+
+ controllerMessages[c, n] = null;
+ }
+ }
+
+ if(programChangeMessages[c] != null)
+ {
+ chasedMessages.Add(programChangeMessages[c]);
+
+ programChangeMessages[c] = null;
+ }
+
+ if(pitchBendMessages[c] != null)
+ {
+ chasedMessages.Add(pitchBendMessages[c]);
+
+ pitchBendMessages[c] = null;
+ }
+
+ if(channelPressureMessages[c] != null)
+ {
+ chasedMessages.Add(channelPressureMessages[c]);
+
+ channelPressureMessages[c] = null;
+ }
+
+ if(polyPressureMessages[c] != null)
+ {
+ chasedMessages.Add(polyPressureMessages[c]);
+
+ polyPressureMessages[c] = null;
+ }
+ }
+
+ OnChased(new ChasedEventArgs(chasedMessages));
+ }
+
+ public void Reset()
+ {
+ for(int c = 0; c <= ChannelMessage.MidiChannelMaxValue; c++)
+ {
+ for(int n = 0; n <= ShortMessage.DataMaxValue; n++)
+ {
+ controllerMessages[c, n] = null;
+ }
+
+ programChangeMessages[c] = null;
+ pitchBendMessages[c] = null;
+ channelPressureMessages[c] = null;
+ polyPressureMessages[c] = null;
+ }
+ }
+
+ protected virtual void OnChased(ChasedEventArgs e)
+ {
+ EventHandler handler = Chased;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Processing/ChannelStopper.cs b/DependenciesCode/Sanford.Multimedia.Midi/Processing/ChannelStopper.cs
new file mode 100644
index 0000000..a663576
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Processing/ChannelStopper.cs
@@ -0,0 +1,211 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class ChannelStopper
+ {
+ private ChannelMessage[,] noteOnMessage;
+
+ private bool[] holdPedal1Message;
+
+ private bool[] holdPedal2Message;
+
+ private bool[] sustenutoMessage;
+
+ private ChannelMessageBuilder builder = new ChannelMessageBuilder();
+
+ public event EventHandler Stopped;
+
+ public ChannelStopper()
+ {
+ int c = ChannelMessage.MidiChannelMaxValue + 1;
+ int d = ShortMessage.DataMaxValue + 1;
+
+ noteOnMessage = new ChannelMessage[c, d];
+
+ holdPedal1Message = new bool[c];
+ holdPedal2Message = new bool[c];
+ sustenutoMessage = new bool[c];
+ }
+
+ public void Process(ChannelMessage message)
+ {
+ switch(message.Command)
+ {
+ case ChannelCommand.NoteOn:
+ if(message.Data2 > 0)
+ {
+ noteOnMessage[message.MidiChannel, message.Data1] = message;
+ }
+ else
+ {
+ noteOnMessage[message.MidiChannel, message.Data1] = null;
+ }
+ break;
+
+ case ChannelCommand.NoteOff:
+ noteOnMessage[message.MidiChannel, message.Data1] = null;
+ break;
+
+ case ChannelCommand.Controller:
+ switch(message.Data1)
+ {
+ case (int)ControllerType.HoldPedal1:
+ if(message.Data2 > 63)
+ {
+ holdPedal1Message[message.MidiChannel] = true;
+ }
+ else
+ {
+ holdPedal1Message[message.MidiChannel] = false;
+ }
+ break;
+
+ case (int)ControllerType.HoldPedal2:
+ if(message.Data2 > 63)
+ {
+ holdPedal2Message[message.MidiChannel] = true;
+ }
+ else
+ {
+ holdPedal2Message[message.MidiChannel] = false;
+ }
+ break;
+
+ case (int)ControllerType.SustenutoPedal:
+ if(message.Data2 > 63)
+ {
+ sustenutoMessage[message.MidiChannel] = true;
+ }
+ else
+ {
+ sustenutoMessage[message.MidiChannel] = false;
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ public void AllSoundOff()
+ {
+ ArrayList stoppedMessages = new ArrayList();
+
+ for(int c = 0; c <= ChannelMessage.MidiChannelMaxValue; c++)
+ {
+ for(int n = 0; n <= ShortMessage.DataMaxValue; n++)
+ {
+ if(noteOnMessage[c, n] != null)
+ {
+ builder.MidiChannel = c;
+ builder.Command = ChannelCommand.NoteOff;
+ builder.Data1 = noteOnMessage[c, n].Data1;
+ builder.Build();
+
+ stoppedMessages.Add(builder.Result);
+
+ noteOnMessage[c, n] = null;
+ }
+ }
+
+ if(holdPedal1Message[c])
+ {
+ builder.MidiChannel = c;
+ builder.Command = ChannelCommand.Controller;
+ builder.Data1 = (int)ControllerType.HoldPedal1;
+ builder.Build();
+
+ stoppedMessages.Add(builder.Result);
+
+ holdPedal1Message[c] = false;
+ }
+
+ if(holdPedal2Message[c])
+ {
+ builder.MidiChannel = c;
+ builder.Command = ChannelCommand.Controller;
+ builder.Data1 = (int)ControllerType.HoldPedal2;
+ builder.Build();
+
+ stoppedMessages.Add(builder.Result);
+
+ holdPedal2Message[c] = false;
+ }
+
+ if(sustenutoMessage[c])
+ {
+ builder.MidiChannel = c;
+ builder.Command = ChannelCommand.Controller;
+ builder.Data1 = (int)ControllerType.SustenutoPedal;
+ builder.Build();
+
+ stoppedMessages.Add(builder.Result);
+
+ sustenutoMessage[c] = false;
+ }
+ }
+
+ OnStopped(new StoppedEventArgs(stoppedMessages));
+ }
+
+ public void Reset()
+ {
+ for(int c = 0; c <= ChannelMessage.MidiChannelMaxValue; c++)
+ {
+ for(int n = 0; n <= ShortMessage.DataMaxValue; n++)
+ {
+ noteOnMessage[c, n] = null;
+ }
+
+ holdPedal1Message[c] = false;
+ holdPedal2Message[c] = false;
+ sustenutoMessage[c] = false;
+ }
+ }
+
+ protected virtual void OnStopped(StoppedEventArgs e)
+ {
+ EventHandler handler = Stopped;
+
+ if(handler != null)
+ {
+ handler(this, e);
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Processing/ChasedEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Processing/ChasedEventArgs.cs
new file mode 100644
index 0000000..678d68b
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Processing/ChasedEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class ChasedEventArgs : EventArgs
+ {
+ private ICollection messages;
+
+ public ChasedEventArgs(ICollection messages)
+ {
+ this.messages = messages;
+ }
+
+ public ICollection Messages
+ {
+ get
+ {
+ return messages;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Processing/StoppedEventArgs.cs b/DependenciesCode/Sanford.Multimedia.Midi/Processing/StoppedEventArgs.cs
new file mode 100644
index 0000000..07cbee7
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Processing/StoppedEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class StoppedEventArgs : EventArgs
+ {
+ private ICollection messages;
+
+ public StoppedEventArgs(ICollection messages)
+ {
+ this.messages = messages;
+ }
+
+ public ICollection Messages
+ {
+ get
+ {
+ return messages;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Sanford.Multimedia.Midi.csproj b/DependenciesCode/Sanford.Multimedia.Midi/Sanford.Multimedia.Midi.csproj
new file mode 100644
index 0000000..a4a202a
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Sanford.Multimedia.Midi.csproj
@@ -0,0 +1,296 @@
+
+
+ Local
+ 8.0.50727
+ 2.0
+ {4269C72A-8D3A-4737-8F89-72EAA33EA9E1}
+ Debug
+ AnyCPU
+
+
+
+
+ Sanford.Multimedia.Midi
+
+
+ JScript
+ Grid
+ IE50
+ false
+ Library
+ Sanford.Multimedia.Midi
+ OnBuildSuccess
+
+
+
+
+
+
+ v2.0
+ 2.0
+
+
+ bin\Debug\
+ false
+ 285212672
+ false
+
+
+ DEBUG;TRACE
+
+
+ true
+ 4096
+ false
+
+
+ false
+ false
+ false
+ false
+ 4
+ full
+ prompt
+
+
+ bin\Release\
+ false
+ 285212672
+ false
+
+
+ DEBUG;TRACE
+
+
+ false
+ 4096
+ false
+
+
+ true
+ false
+ false
+ false
+ 4
+ none
+ prompt
+
+
+
+ False
+ ..\Dependencies\Sanford.Collections.dll
+
+
+ False
+ ..\Dependencies\Sanford.Multimedia.dll
+
+
+ False
+ ..\Dependencies\Sanford.Multimedia.Timers.dll
+
+
+ False
+ ..\Dependencies\Sanford.Threading.dll
+
+
+ System
+
+
+ System.Data
+
+
+
+
+ System.XML
+
+
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+
+
+ Code
+
+
+
+
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+
+ Code
+
+
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+
+ Code
+
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+
+
+
+ Code
+
+
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Code
+
+
+
+ Form
+
+
+ DeviceDialog.cs
+
+
+ Form
+
+
+ InputDeviceDialog.cs
+
+
+ Form
+
+
+ OutputDeviceDialog.cs
+
+
+ Component
+
+
+ Component
+
+
+ Form
+
+
+ PianoControlDialog.cs
+
+
+
+
+
+ Designer
+ DeviceDialog.cs
+
+
+ Designer
+ InputDeviceDialog.cs
+
+
+ Designer
+ OutputDeviceDialog.cs
+
+
+ Designer
+ PianoControlDialog.cs
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/MidiEvent.cs b/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/MidiEvent.cs
new file mode 100644
index 0000000..cc3e84b
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/MidiEvent.cs
@@ -0,0 +1,148 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class MidiEvent
+ {
+ private object owner = null;
+
+ private int absoluteTicks;
+
+ private IMidiMessage message;
+
+ private MidiEvent next = null;
+
+ private MidiEvent previous = null;
+
+ internal MidiEvent(object owner, int absoluteTicks, IMidiMessage message)
+ {
+ #region Require
+
+ if(owner == null)
+ {
+ throw new ArgumentNullException("owner");
+ }
+ else if(absoluteTicks < 0)
+ {
+ throw new ArgumentOutOfRangeException("absoluteTicks", absoluteTicks,
+ "Absolute ticks out of range.");
+ }
+ else if(message == null)
+ {
+ throw new ArgumentNullException("e");
+ }
+
+ #endregion
+
+ this.owner = owner;
+ this.absoluteTicks = absoluteTicks;
+ this.message = message;
+ }
+
+ internal void SetAbsoluteTicks(int absoluteTicks)
+ {
+ this.absoluteTicks = absoluteTicks;
+ }
+
+ internal object Owner
+ {
+ get
+ {
+ return owner;
+ }
+ }
+
+ public int AbsoluteTicks
+ {
+ get
+ {
+ return absoluteTicks;
+ }
+ }
+
+ public int DeltaTicks
+ {
+ get
+ {
+ int deltaTicks;
+
+ if(Previous != null)
+ {
+ deltaTicks = AbsoluteTicks - previous.AbsoluteTicks;
+ }
+ else
+ {
+ deltaTicks = AbsoluteTicks;
+ }
+
+ return deltaTicks;
+ }
+ }
+
+ public IMidiMessage MidiMessage
+ {
+ get
+ {
+ return message;
+ }
+ }
+
+ internal MidiEvent Next
+ {
+ get
+ {
+ return next;
+ }
+ set
+ {
+ next = value;
+ }
+ }
+
+ internal MidiEvent Previous
+ {
+ get
+ {
+ return previous;
+ }
+ set
+ {
+ previous = value;
+ }
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/MidiFileProperties.cs b/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/MidiFileProperties.cs
new file mode 100644
index 0000000..54cede3
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/MidiFileProperties.cs
@@ -0,0 +1,384 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Defintes constants representing SMPTE frame rates.
+ ///
+ public enum SmpteFrameRate
+ {
+ Smpte24 = 24,
+ Smpte25 = 25,
+ Smpte30Drop = 29,
+ Smpte30 = 30
+ }
+
+ ///
+ /// The different types of sequences.
+ ///
+ public enum SequenceType
+ {
+ Ppqn,
+ Smpte
+ }
+
+ ///
+ /// Represents MIDI file properties.
+ ///
+ internal class MidiFileProperties
+ {
+ private const int PropertyLength = 2;
+
+ private static readonly byte[] MidiFileHeader =
+ {
+ (byte)'M',
+ (byte)'T',
+ (byte)'h',
+ (byte)'d',
+ 0,
+ 0,
+ 0,
+ 6
+ };
+
+ private int format = 1;
+
+ private int trackCount = 0;
+
+ private int division = PpqnClock.PpqnMinValue;
+
+ private SequenceType sequenceType = SequenceType.Ppqn;
+
+ public MidiFileProperties()
+ {
+ }
+
+ public void Read(Stream strm)
+ {
+ #region Require
+
+ if(strm == null)
+ {
+ throw new ArgumentNullException("strm");
+ }
+
+ #endregion
+
+ format = trackCount = division = 0;
+
+ FindHeader(strm);
+ Format = (int)ReadProperty(strm);
+ TrackCount = (int)ReadProperty(strm);
+ Division = (int)ReadProperty(strm);
+
+ #region Invariant
+
+ AssertValid();
+
+ #endregion
+ }
+
+ private void FindHeader(Stream stream)
+ {
+ bool found = false;
+ int result;
+
+ while(!found)
+ {
+ result = stream.ReadByte();
+
+ if(result == 'M')
+ {
+ result = stream.ReadByte();
+
+ if(result == 'T')
+ {
+ result = stream.ReadByte();
+
+ if(result == 'h')
+ {
+ result = stream.ReadByte();
+
+ if(result == 'd')
+ {
+ found = true;
+ }
+ }
+ }
+ }
+
+ if(result < 0)
+ {
+ throw new MidiFileException("Unable to find MIDI file header.");
+ }
+ }
+
+ // Eat the header length.
+ for(int i = 0; i < 4; i++)
+ {
+ if(stream.ReadByte() < 0)
+ {
+ throw new MidiFileException("Unable to find MIDI file header.");
+ }
+ }
+ }
+
+ private ushort ReadProperty(Stream strm)
+ {
+ byte[] data = new byte[PropertyLength];
+
+ int result = strm.Read(data, 0, data.Length);
+
+ if(result != data.Length)
+ {
+ throw new MidiFileException("End of MIDI file unexpectedly reached.");
+ }
+
+ if(BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(data);
+ }
+
+ return BitConverter.ToUInt16(data, 0);
+ }
+
+ public void Write(Stream strm)
+ {
+ #region Require
+
+ if(strm == null)
+ {
+ throw new ArgumentNullException("strm");
+ }
+
+ #endregion
+
+ strm.Write(MidiFileHeader, 0, MidiFileHeader.Length);
+ WriteProperty(strm, (ushort)Format);
+ WriteProperty(strm, (ushort)TrackCount);
+ WriteProperty(strm, (ushort)Division);
+ }
+
+ private void WriteProperty(Stream strm, ushort property)
+ {
+ byte[] data = BitConverter.GetBytes(property);
+
+ if(BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(data);
+ }
+
+ strm.Write(data, 0, PropertyLength);
+ }
+
+ private static bool IsSmpte(int division)
+ {
+ bool result;
+ byte[] data = BitConverter.GetBytes((short)division);
+
+ if(BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(data);
+ }
+
+ if((sbyte)data[0] < 0)
+ {
+ result = true;
+ }
+ else
+ {
+ result = false;
+ }
+
+ return result;
+ }
+
+ [Conditional("DEBUG")]
+ private void AssertValid()
+ {
+ if(trackCount > 1)
+ {
+ Debug.Assert(Format == 1 || Format == 2);
+ }
+
+ if(IsSmpte(Division))
+ {
+ Debug.Assert(SequenceType == SequenceType.Smpte);
+ }
+ else
+ {
+ Debug.Assert(SequenceType == SequenceType.Ppqn);
+ Debug.Assert(Division % PpqnClock.PpqnMinValue == 0);
+ }
+ }
+
+ public int Format
+ {
+ get
+ {
+ return format;
+ }
+ set
+ {
+ #region Require
+
+ if(value < 0 || value > 3)
+ {
+ throw new ArgumentOutOfRangeException("Format", value,
+ "MIDI file format out of range.");
+ }
+ else if(value == 0 && trackCount > 1)
+ {
+ throw new ArgumentException(
+ "MIDI file format invalid for this track count.");
+ }
+
+ #endregion
+
+ format = value;
+
+ #region Invariant
+
+ AssertValid();
+
+ #endregion
+ }
+ }
+
+ public int TrackCount
+ {
+ get
+ {
+ return trackCount;
+ }
+ set
+ {
+ #region Require
+
+ if(value < 0)
+ {
+ throw new ArgumentOutOfRangeException("TrackCount", value,
+ "Track count out of range.");
+ }
+ else if(value > 1 && Format == 0)
+ {
+ throw new ArgumentException(
+ "Track count invalid for this format.");
+ }
+
+ #endregion
+
+ trackCount = value;
+
+ #region Invariant
+
+ AssertValid();
+
+ #endregion
+ }
+ }
+
+ public int Division
+ {
+ get
+ {
+ return division;
+ }
+ set
+ {
+ if(IsSmpte(value))
+ {
+ byte[] data = BitConverter.GetBytes((short)value);
+
+ if(BitConverter.IsLittleEndian)
+ {
+ Array.Reverse(data);
+ }
+
+ if((sbyte)data[0] != -(int)SmpteFrameRate.Smpte24 &&
+ (sbyte)data[0] != -(int)SmpteFrameRate.Smpte25 &&
+ (sbyte)data[0] != -(int)SmpteFrameRate.Smpte30 &&
+ (sbyte)data[0] != -(int)SmpteFrameRate.Smpte30Drop)
+ {
+ throw new ArgumentException("Invalid SMPTE frame rate.");
+ }
+ else
+ {
+ sequenceType = SequenceType.Smpte;
+ }
+ }
+ else
+ {
+ if(value % PpqnClock.PpqnMinValue != 0)
+ {
+ throw new ArgumentException(
+ "Invalid pulses per quarter note value.");
+ }
+ else
+ {
+ sequenceType = SequenceType.Ppqn;
+ }
+ }
+
+ division = value;
+
+ #region Invariant
+
+ AssertValid();
+
+ #endregion
+ }
+ }
+
+ public SequenceType SequenceType
+ {
+ get
+ {
+ return sequenceType;
+ }
+ }
+ }
+
+ public class MidiFileException : ApplicationException
+ {
+ public MidiFileException(string message) : base(message)
+ {
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/RecordingSession.cs b/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/RecordingSession.cs
new file mode 100644
index 0000000..26c5f4f
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/RecordingSession.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Sanford.Multimedia.Midi
+{
+ public class RecordingSession
+ {
+ private IClock clock;
+
+ private List buffer = new List();
+
+ private Track result = new Track();
+
+ public RecordingSession(IClock clock)
+ {
+ this.clock = clock;
+ }
+
+ public void Build()
+ {
+ result = new Track();
+
+ buffer.Sort(new TimestampComparer());
+
+ foreach(TimestampedMessage tm in buffer)
+ {
+ result.Insert(tm.ticks, tm.message);
+ }
+ }
+
+ public void Clear()
+ {
+ buffer.Clear();
+ }
+
+ public Track Result
+ {
+ get
+ {
+ return result;
+ }
+ }
+
+ public void Record(ChannelMessage message)
+ {
+ if(clock.IsRunning)
+ {
+ buffer.Add(new TimestampedMessage(clock.Ticks, message));
+ }
+ }
+
+ public void Record(SysExMessage message)
+ {
+ if(clock.IsRunning)
+ {
+ buffer.Add(new TimestampedMessage(clock.Ticks, message));
+ }
+ }
+
+ private struct TimestampedMessage
+ {
+ public int ticks;
+
+ public IMidiMessage message;
+
+ public TimestampedMessage(int ticks, IMidiMessage message)
+ {
+ this.ticks = ticks;
+ this.message = message;
+ }
+ }
+
+ private class TimestampComparer : IComparer
+ {
+ #region IComparer Members
+
+ public int Compare(TimestampedMessage x, TimestampedMessage y)
+ {
+ if(x.ticks > y.ticks)
+ {
+ return 1;
+ }
+ else if(x.ticks < y.ticks)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/Sequence.cs b/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/Sequence.cs
new file mode 100644
index 0000000..7cd339c
--- /dev/null
+++ b/DependenciesCode/Sanford.Multimedia.Midi/Sequencing/Sequence.cs
@@ -0,0 +1,773 @@
+#region License
+
+/* Copyright (c) 2006 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: jabberdabber@hotmail.com
+ */
+
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+
+namespace Sanford.Multimedia.Midi
+{
+ ///
+ /// Represents a collection of Tracks.
+ ///
+ public sealed class Sequence : IComponent, ICollection