Click here to Skip to main content
15,894,090 members
Articles / Programming Languages / C#

ProjectMIDI: an extensible set of small MIDI .NET programs

Rate me:
Please Sign up or sign in to vote.
4.70/5 (14 votes)
24 Jan 200626 min read 85.4K   3.4K   57  
This article describes how multiple .NET assemblies work together to control MIDI devices in a live performance environment.
using System;
using System.IO;
using System.Xml;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Diagnostics;
using ProjectMidiNS;
using System.Threading;
using MidiNS;

// A good 'event' for the TD8 is the Inc/Dec buttons
// The Inc/Dec buttons can be used to page forward through
// each page and song.

// Patches will be cached.
// When the song collection is loaded, the QuerySongs and CacheSongs
// will be fired, passing the SongCollection object.  This allows
// each device to preload its patch files.
// When the request is received to select a patch, the filename will
// be compared, and reloaded if not equal.  Otherwise the previously
// loaded data will be used.

namespace TD8NS
{
	#region structs
	// Define the data layout for the TD-8.
	// Note:	this is for the program use only, and may not align bit-for-bit with the
	//			actual TD-8 SysEx or internal layout.
	//[StructLayout(LayoutKind.Sequential)]
	public struct TD8Common
	{
		#region enums
		public enum enumStudioType
		{
			Beach = 0,
			Living,
			Bath,
			Studio,
			Garage,
			Locker,
			Theater,
			Cave,
			Gym,
			Stadium
		};
		public enum enumWallType
		{
			Wood = 0,
			Plaster,
			Glass
		};
		public enum enumRoomSize
		{
			Small = 1,
			Medium,
			Large
		}
		public enum enumEQLowFreq
		{
			Fo200Hz = 0,
			Fo400Hz = 1
		}
		public enum enumEQHighFreq
		{
			Fo3kHz = 0,
			Fo6kHz = 1
		}
		#endregion

		#region variables
		//[MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
		private string	name;		// Max 8 characters allowed
		private enumStudioType studioType;
		private byte studioLevel;
		private enumWallType wallType;
		private enumRoomSize roomSize;
		private enumEQLowFreq	eqLowFreq;
		private byte			eqLowGain;			// 0 - 24 (-12 to +12 dB)
		private enumEQHighFreq	eqHighFreq;
		private byte			eqHighGain;			// 0 - 24 (-12 to +12 dB)
		private bool			ambienceOnOff;
		private	bool			eqOnOff;
		private bool			brushOnOff;	
		private byte			pedalHiHatVolume;	// 0 - 15
		private byte			pedalBendRange;		// 0-48 (-24 to +24 semitones)
		private byte			masterVolume;		// 0 - 127
		private byte	ambienceGroupKitSendLevel;	// 0 - 127
		private byte	ambienceGroupPercSendLevel;	// 0 - 127
		private byte	ambienceGroupPartSendLevel;	// 0 - 127
		#endregion

		#region Properties
		public string Name 
		{ 
			get { return name; }
			set { name = value; }
		}
		public enumStudioType StudioType 
		{ 
			get { return studioType; }
			set { studioType = value; }
		}
		public byte StudioLevel 
		{ 
			get { return studioLevel; } 
			set { studioLevel = value; }
		}
		public enumWallType WallType 
		{ 
			get { return wallType; } 
			set { wallType = value; }
		}
		public enumRoomSize RoomSize 
		{ 
			get { return roomSize; } 
			set { roomSize = value; }
		}
		public enumEQLowFreq EQLowFreq 
		{
			get { return eqLowFreq; }
			set { eqLowFreq = value; }
		}
		public byte EQLowGain					// 0 - 24 (-12 to +12 dB)
		{
			get { return eqLowGain; }
			set { eqLowGain = value; }
		}
		public enumEQHighFreq EQHighFreq
		{
			get { return eqHighFreq; }
			set { eqHighFreq = value; }
		}
		public byte EQHighGain					// 0 - 24 (-12 to +12 dB)
		{
			get { return eqHighGain; }
			set { eqHighGain = value; }
		}
		public bool AmbienceOnOff
		{
			get { return ambienceOnOff; }
			set { ambienceOnOff = value; }
		}
		public bool EQOnOff
		{
			get { return eqOnOff; }
			set { eqOnOff = value; }
		}
		public bool BrushOnOff
		{
			get { return brushOnOff; }
			set { brushOnOff = value; }
		}
		public byte PedalHiHatVolume	// 0 - 15
		{
			get { return pedalHiHatVolume; }
			set { pedalHiHatVolume = value; }
		}
		public byte PedalBendRange		// 0-48 (-24 to +24 semitones)
		{
			get { return pedalBendRange; }
			set { pedalBendRange = value; }
		}
		public byte MasterVolume		// 0 - 127
		{
			get { return masterVolume; }
			set { masterVolume = value; }
		}
		public byte AmbienceGroupKitSendLevel		// 0 - 127
		{
			get { return ambienceGroupKitSendLevel; }
			set { ambienceGroupKitSendLevel = value; }
		}
		public byte AmbienceGroupPercSendLevel		// 0 - 127
		{
			get { return ambienceGroupPercSendLevel; }
			set { ambienceGroupPercSendLevel = value; }
		}
		public byte AmbienceGroupPartSendLevel		// 0 - 127
		{
			get { return ambienceGroupPartSendLevel; }
			set { ambienceGroupPartSendLevel = value; }
		}
		#endregion
	}

	//[StructLayout(LayoutKind.Sequential)]
	//
	public class TD8PartialPad
	{
		#region variables
		private uint instrument;			// 0-1023
		private int pitch;					// 0-960 (-4800 - +4800 cent, 10 cent steps)
		private byte decay;					// 0-62 (-31 = +31)
		private uint playPatternNumber;		// 0-800 (0=off)
		private byte gateTime;				// 0-80 (0.1 second steps) 
		private byte noteNum;				// 0-127
		private bool padPatternVelocity;	// 0-1
		private byte level;					// 0-127
		private byte ambienceSendLevel;		// 0-127
		private bool pitchCtrlAssign;		//
		#endregion
		#region Properties
		public uint Instrument 
		{ 
			get { return instrument; } 
			set { instrument = value; }
		}
		public int Pitch 
		{ 
			get { return pitch; } 
			set { pitch = value; }
		}
		public byte Decay 
		{ 
			get { return decay; } 
			set { decay = value; }
		}
		public uint PlayPatternNumber 
		{ 
			get { return playPatternNumber; } 
			set { playPatternNumber = value; }
		}
		public byte GateTime
		{ 
			get { return gateTime; } 
			set { gateTime = value; }
		}
		public byte NoteNum				// 0-127
		{
			get { return noteNum; }
			set { noteNum = value; }
		}
		public bool PadPatternVelocity	// 0-1
		{
			get { return padPatternVelocity; }
			set { padPatternVelocity = value; }
		}
		public byte Level					// 0-127
		{
			get { return level; }
			set { level = value; }
		}
		public byte AmbienceSendLevel		// 0-127
		{
			get { return ambienceSendLevel; }
			set { ambienceSendLevel = value; }
		}
		public bool PitchCtrlAssign
		{
			get { return pitchCtrlAssign; }
			set { pitchCtrlAssign = value; }
		}
		#endregion
	}

	//public struct TD8Pad
	public class TD8Pad
	{
		public enum enumShellDepth
		{
			Normal = 0,
			Deep = 1,
			Deep1 = 1,
			Deep2 = 2,
			Deep3 = 3,
			Deep4 = 4
		}
		public enum enumHeadType
		{
			Clear = 0,
			Coated = 1,
			PinStripe = 2
		}
		public enum enumMuffling
		{
			Off = 0,
			Tape1 = 1,		// Kick
			Tape2 = 2,		// Kick
			Blanket = 3,	// Kick
			Doughnuts1 = 3,	// Snare
			Felt1 = 3,		// Tom
			Weight = 4,		// Kick
			Doughnuts2 = 4,	// Snare
			Felt2 = 4		// Tom
		}
		public enum enumStrainerAdjustment
		{
			Off = 0,
			Loose,
			Medium,
			Tight
		}

		//[MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
		public TD8PartialPad[] Pad;		// was Head
		//public TD8PartialPad Head;
		//public TD8PartialPad Rim;
		private byte pan;
		private enumShellDepth shellDepth;
		private enumHeadType headType;
		private enumMuffling muffling;
		private enumStrainerAdjustment strainerAdjustment;

		public TD8Pad()
		{
			Pad = new TD8PartialPad[2];
			Pad[0] = new TD8PartialPad();
			Pad[1] = new TD8PartialPad();
			//Head = new TD8PartialPad();
			//Rim = new TD8PartialPad();
		}
		public byte Pan
		{
			get { return pan; }
			set { pan = value; }
		}
		public enumShellDepth ShellDepth
		{
			get { return shellDepth; }
			set { shellDepth = value; }
		}
		public enumHeadType HeadType
		{
			get { return headType; }
			set { headType = value; }
		}
		public enumMuffling Muffling
		{
			get { return muffling; }
			set { muffling = value; }
		}
		public enumStrainerAdjustment StrainerAdjustment
		{
			get { return strainerAdjustment; }
			set { strainerAdjustment = value; }
		}
	}

	public class TD8Kit
	{
		public const int NumPads = 12;
		public TD8Common Common = new TD8Common();
		public TD8Pad[] Pads = new TD8Pad[NumPads];			// 12 pads per kit

		public TD8Kit()
		{
			for(int x=0; x<NumPads; x++) Pads[x] = new TD8Pad();
		}
	}
	#endregion

	/// <summary>
	/// This class implements the TD8 MIDI device.
	/// </summary>
	[ProjectMidiAssemblyAttribute(AssemblyType.Device, "TD8" )]
	public class TD8 : IProjectMidi, IProjectMidiHideShow
	{
	
		#region Constants
		const string name = "TD8";
		const int	DefaultKitNum = 63;			// The top kit #64 is the default kit.
		const int	DevNum = 0x10;
		const int	DevMidiChan = 9;			// Midi channel 10 (0 based)
		const int	patchCommonLength = 25;
		const int	patchPadLength = 43;
		public const int NumGroups = 16;
		public const int NumInstruments = 1025;
		const byte	EOX = 0xf7;
		//public const string RegistryPath = @"Software\ProjectMidi\MidiDevices";
		//const string TD8TemplateSysExFilePathKey = "TD8TemplateSysExFilePath";
		//const string TD8SysExFilesDirKey = "TD8SysExFilesDir";
		const string TD8FolderName = "TD8";
		const int	SysExPrefixLen = 10;		// SysEx prefix (f0, 41, dev, 00, 20, 12, aa, aa, aa, aa)
		const int	NumSysExCommonBytes = 37;	// 'Common' parameters size (SysExPrefixLen + 25 + chksum + 0xf7)
		const int	NumSysExPadBytes = 55;
		const int	BassDrumNoteNum = 36;
		const int	SnareDrumNoteNum = 38;
		const int	SnareDrumRimNoteNum = 40;
		const int	HighHatNoteNum = 46;
		const int	HighTomNoteNum = 48;
		const int	MidTomNoteNum = 45;
		const int	LowTomNoteNum = 41;
		const int	FloorTomNoteNum = 31;
		const int	CrashCymbalNoteNum = 49;
		const int	SplashCymbalNoteNum = 57;
		const int	RideCymbalNoteNum = 51;
		const int	CowbellNoteNum = 32;
		public const int	KickPadIndex = 0; 
		public const int	Kick2PadIndex = 1; 
		public const int	SnarePadIndex = 2;
		public const int	Tom1PadIndex = 3; 
		public const int	Tom2PadIndex = 4; 
		public const int	Tom3PadIndex = 5; 
		public const int	Tom4PadIndex = 10; 
		public const int	HihatPadIndex = 6; 
		public const int	CrashPadIndex = 7; 
		public const int	SplashPadIndex = 8; 
		public const int	RidePadIndex = 9; 
		public const int	CowbellPadIndex = 11; 
		#endregion

		#region Instrument name arrays
		#region vkickGroup 1-90
		public static string[] vkickGroup = {		// 1 - 90
												"Dbl Head Kick",		
												"Sharp Kick",
												"Acoustic Kick",
												"Meat Kick",
												"R8 Low Kick",
												"R8 Dry Kick",
												"WD Beater Kick",
												"Open Kick",
												"Vintage Kick",
												"26 Deep Kick",	// 10
												"Thick Hd Kick",
												"Round Kick",
												"Medium Kick",
												"Big Room Kick",
												"Big Kick",
												"Big Low Kick",
												"Studio 1 Kick",
												"Studio 2 Kick",
												"Studio 3 Kick",
												"Studio 4 Kick",	// 20
												"Studio 5 Kick",
												"Studio 6 Kick",
												"Studio 7 Kick",
												"Studio 8 Kick",
												"Buzz 1 Kick",
												"Buzz 2 Kick",
												"Buzz 3 Kick",
												"Buzz 4 Kick",
												"Buzz 5 Kick",
												"Room 1 Kick",	// 30
												"Room 2 Kick",
												"Room 3 Kick",
												"Room 4 Kick",
												"Room 5 Kick",
												"Room 6 Kick",
												"Room 7 Kick",
												"Amb 1 Kick",
												"Amb 2 Kick",
												"Amb 3 Kick",
												"Amb 4 Kick",	// 40
												"Solid 1 Kick",
												"Solid 2 Kick",
												"Solid 3 Kick",
												"Jazz 1 Kick",
												"18 Jazz Kick",
												"Brush Hit Kick",
												"Wood 1 Kick",
												"Wood 2 Kick",
												"Wood 3 Kick",	// 50
												"Wood 4 Kick",
												"Maple 1 Kick",
												"Maple 2 Kick",
												"Oak Kick",
												"Birch Kick",
												"Rose Wood Kick",
												"One Ply Kick",
												"Oyster Kick",
												"Dry Kick",
												"Dry Medium Kick",	// 60
												"Dry Hard Kick",
												"Deep Dry Kick",
												"Fusion Kick",
												"Sand Bag Kick",
												"BasketBall Kick",
												"Mondo Kick",
												"MdVrb1 Kick",
												"MdVrb2 Kick",
												"Sizzle Kick",
												"Box Kick",		// 70
												"Ninja Kick",
												"Dance Kick",
												"House Kick",
												"Pillow Kick",
												"Rap Kick",
												"TR808 Kick",
												"808 Hard Kick",
												"808 Boom Kick",
												"808 Noise Kick",
												"TR909 Kick",	// 80
												"909 Wood Kick",
												"909 HdatKick",
												"Elephant Kick",
												"Cattle Kick",
												"Door Kick",
												"Punch Kick",
												"Machine Kick",
												"Broken Kick",
												"Bend Up Kick",
												"Hard Noise Kick"	// 90
											};
		#endregion
		#region kickGroup 91-129
		public static string[] kickGroup = {	// 91 - 129
											   "R8 Solid Kick",
											   "R8 Solid Kick",
											   "Thin Head Kick",
											   "Tight Kick",
											   "Chunk Kick",
											   "Gate Kick",
											   "Giant Kick",
											   "Inside Kick",
											   "Std1 1 Kick",
											   "Std1 2 Kick",
											   "Std2 1 Kick",	// 100
											   "Std2 2 Kick",
											   "Room 8 Kick",
											   "Room 9 Kick",
											   "Power Kick 1",
											   "Power Kick 2",
											   "Jazz 3 Kick",
											   "Jazz 4 Kick",
											   "Brush Kick",
											   "Elec 1 Kick",
											   "Elec 2 Kick",		// 110
											   "ElBend Kick",
											   "Plastic 1 Kick",
											   "Plastic 2 Kick",
											   "Gabba Kick",
											   "Gabba2 Kick",
											   "Tail Kick",
											   "Jungle Kick",
											   "HipHop Kick",
											   "LoFi 1 Kick",
											   "LoFi 2 Kick",		// 120
											   "LoFi 3 Kick",
											   "LoFi 4 Kick",
											   "Noisy Kick",
											   "Splat Kick",
											   "Scratch 1 Kick",
											   "Scratch 2 Kick",
											   "Hi-Q Kick",
											   "Space Kick",
											   "Syn Bass Kick"		// 129
										   };
		#endregion
		#region vsnareGroup 130-235
		public static string[] vsnareGroup = {	// 130 - 235
												 "Custom Snare (pos)",	// 130
												 "Cstm Rimshot",
												 "CstmBr S(pos)",
												 "CstmBrRS",
												 "CstmSt S(pos)",
												 "CstmStRS",
												 "Picolo1S(pos)",
												 "Pco1 RS",
												 "Pco1BR S(pos)",
												 "Pco1BrRS",
												 "Pco1St S(pos)",	// 140
												 "Pco1StRS",
												 "Picolo2S(pos)",
												 "Pco2 RS",
												 "Pco2Br S(pos)",
												 "Pco2BrRs",
												 "Pco2St S(pos)",
												 "Pco2StRs",
												 "Picolo3S(pos)",
												 "Pco3 RS",
												 "Pco3Br S(pos)",	// 150
												 "Pco3BrRs",
												 "Pco3St S(pos)",
												 "Pco3StRs",
												 "Medium1S(pos)",
												 "Med1 RS",
												 "Med1 XS(x-stick)",
												 "Med1Br S(pos)",
												 "Med1BrRS",
												 "Med1BrXS(x-stick)",
												 "Med1St S(pos)",	// 160
												 "Med1StRS",
												 "Med1StXS(x-stick)",
												 "Medium2S(pos)",
												 "Med2 RS",
												 "Med2Br S(pos)",
												 "Med2BrRS",
												 "Med2St S(pos)",
												 "Med2StRS",
												 "Medium3S(pos)",
												 "Med3 RS",			// 170
												 "Med3Br S(pos)",
												 "Med3BrRS",
												 "Med3St S(pos)",
												 "Med3StRS",
												 "Medium4S(pos)",
												 "Med4 RS",
												 "Med4Br S(pos)",
												 "Med4BrRS",
												 "Med4St S(pos)",
												 "Med4StRS",			// 180
												 "Fat1 S(pos)",
												 "Fat1 RS",
												 "Fat1Br S(pos)",
												 "Fat1BrRS",
												 "Fat1St S(pos)",
												 "Fat1StRS",
												 "Fat2 S(pos)",
												 "Fat2 RS",
												 "Fat2Br S(pos)",
												 "Fat2BrRS",			// 190
												 "Fat2St S(pos)",
												 "Fat2StRS",
												 "AcusticS(pos)",
												 "Acus RS",
												 "AcusBr S(pos)",
												 "AcusBrRS",
												 "AcusSt S(pos)",
												 "AcusStRS",
												 "VintageS(pos)",
												 "Vntg RS",			// 200
												 "VntgBr S(pos)",
												 "VntgBrRS",
												 "VntgSt S(pos)",
												 "VntgStRS",
												 "Comp S(pos)",
												 "Comp RS",
												 "CompBr S(pos)",
												 "CompBrRS",
												 "CompSt S(pos)",
												 "CompStRS",			// 210
												 "Jazz S",
												 "Jazz RS",
												 "Jazz XS(x-stick)",
												 "JazzBr S",
												 "JazzBrRS",
												 "JazzBrXS(x-stick)",
												 "JazzSt S",
												 "JazzStRS",
												 "JazzStXS(x-stick)",
												 "Dirty S(pos)",		// 220
												 "Drty RS",
												 "DrtyBr S(pos)",
												 "DrtyBrRS",
												 "DrtySt S(pos)",
												 "DrtyStRS",
												 "13 S",
												 "13 RS",
												 "Birch S",
												 "Birch RS",
												 "TD7Mpl S",			// 230
												 "TD7MplRS",
												 "Ballad S",
												 "Brush1 S(sweep)",
												 "Brush2 S(sweep)",
												 "Brush3 S(sweep)"
											 };
		#endregion
		#region snareGroup 236-324
		public static string[] snareGroup = {		// 236 - 324
												"Brush Tap",
												"Brush Slap",
												"Brush Swirl",
												"Brush TMB Snare *sweep",
												"MIDI Brush 1 Snare",			//240
												"MIDI Brush 2 Snare",
												"MIDI Brush 3 Snare",
												"Boston Snare",
												"Boston RimShot",
												"Bronze Snare",
												"Bronze RimShot",
												"Bronze 2 Snare",
												"Bronze 2 RimShot",
												"Birch 2 Snare",
												"Copper Snare",			// 250
												"Copper 2 Snare",
												"10 inch Snare",
												"L.A. Snare",
												"London Snare",
												"Ring Snare",
												"Ring Rimshot",
												"Rock Snare",
												"Rock RimShot",
												"R8 Maple Snare",
												"R8 Maple RimShot",		// 260
												"Bigshot Snare",
												"Standard1 1 Snare",
												"Standard1 2 Snare",
												"Standard2 1 Snare",
												"Standard2 2 Snare",
												"Room 1 Snare",
												"Room 2 Snare",
												"Power 1 Snare",
												"Power 2 Snare",
												"Gate Snare",		// 270
												"Jazz 2 Snare",
												"Jazz 3 Snare",
												"Funk Snare",
												"Funk RimShot",
												"Bop Snare",
												"Bop RimShot",
												"Picolo 5 Snare",
												"Picolo 5 RimShot",
												"Picolo 6 Snare",
												"Picolo 6 RimShot",		// 280
												"Medium 5 Snare",
												"Medium 5 RimShot",
												"Medium 6 Snare",
												"Medium 6 RimShot",
												"Medium 7 Snare",
												"Medium 7 RimShot",
												"Medium 8 Snare",
												"Medium 8 RimShot",
												"Fat 3 Snare",
												"Fat 3 RimShot",		// 290
												"Fat 4 Snare",
												"Fat 4 RimShot",
												"Dynamic Snare",
												"Dynamic RimShot",
												"Roll Snare",
												"Buzz Snare",
												"Dopin 1 Snare",
												"Dopin 2 Snare",
												"Raggae Snare",
												"Cruddy Snare",			// 300
												"Dance 1 Snare",
												"Dance 2 Snare",
												"House Snare",
												"House DPN Snare",
												"Clap! Snare",
												"Whack Snare",
												"TR808 Snare",
												"TR909 Snare",
												"Electric 1 Snare",
												"Electric 2 Snare",		// 310
												"Electric 3 Snare",
												"El Noise Snare",
												"Hip Hop Snare",
												"Hip Hop 2 Snare",
												"LoFi Snare",
												"LoFi RimShot",
												"Radio Snare",
												"Cross-Stick 1",
												"Cross-Stick 2",
												"Cross-Stick 3",		// 320
												"Cross-Stick 4",
												"Cross-Stick 5",
												"Cross-Stick 6",
												"808 Cross-Stick"
											};
		#endregion
		#region vtomGroup 325-484
		public static string[] vtomGroup = { 	// 325 - 484
											   "Oyster Tom1",
											   "Oyster Tom2",
											   "Oyster Tom3",
											   "Oyster Tom4",
											   "Comp Tom1",
											   "Comp Tom2",		// 330
											   "Comp Romp3",
											   "Comp Tom4",
											   "Fibre Tom1",
											   "Fibre Tom2",
											   "Fibre Tom3",
											   "Fibre Tom4",
											   "Dry 1 Tom1",
											   "Dry 1 Tom2",
											   "Dry 1 Tom3",
											   "Dry 1 Tom4",		// 340
											   "Dry 2 Tom1",
											   "Dry 2 Tom2",
											   "Dry 2 Tom3",
											   "Dry 2 Tom4",
											   "Maple Tom1",
											   "Maple Tom2",
											   "Maple Tom3",
											   "Maple Tom4",
											   "Rose Tom1",
											   "Rose Tom2",		// 350
											   "Rose Tom3",
											   "Rose Tom4",
											   "Sakura Tom1",
											   "Sakura Tom2",
											   "Sakura Tom3",
											   "Sakura Tom4",
											   "Jazz 1 Tom1",
											   "Jazz 1 Tom2",
											   "Jazz 1 Tom3",
											   "Jazz 1 Tom4",		// 360
											   "Jazz 2 Tom1",
											   "Jazz 2 Tom2",
											   "Jazz 2 Tom3",
											   "Jazz 2 Tom4",
											   "Buzz 1 Tom1",
											   "Buzz 1 Tom2",
											   "Buzz 1 Tom3",
											   "Buzz 1 Tom4",
											   "Buzz 2 Tom1",
											   "Buzz 2 Tom2",		// 370
											   "Buzz 2 Tom3",
											   "Buzz 2 Tom4",
											   "Buzz 3 Tom1",
											   "Buzz 3 Tom2",
											   "Buzz 3 Tom3",
											   "Buzz 3 Tom4",
											   "Buzz 4 Tom1",
											   "Buzz 4 Tom2",
											   "Buzz 4 Tom3",
											   "Buzz 4 Tom4",		// 380
											   "Natural Tom1",
											   "Natural Tom2",
											   "Natural Tom3",
											   "Natural Tom4",
											   "Natural 2 Tom1",
											   "Natural 2 Tom2",
											   "Natural 2 Tom3",
											   "Natural 2 Tom4",
											   "Studio Tom1",
											   "Studio Tom2",		// 390
											   "Studio Tom3",
											   "Studio Tom4",
											   "Slap Tom1",
											   "Slap Tom2",
											   "Slap Tom3",
											   "Slap Tom4",
											   "Room 1 Tom1",
											   "Room 1 Tom2",
											   "Room 1 Tom3",
											   "Room 1 Tom4",		// 400
											   "Room 2 Tom1",
											   "Room 2 Tom2",
											   "Room 2 Tom3",
											   "Room 2 Tom4",
											   "Room 3 Tom1",
											   "Room 3 Tom2",
											   "Room 3 Tom3",
											   "Room 3 Tom4",
											   "Room 4 Tom1",
											   "Room 4 Tom2",		// 410
											   "Room 4 Tom3",
											   "Room 4 Tom4",
											   "Room 5 Tom1",
											   "Room 5 Tom2",
											   "Room 5 Tom3",
											   "Room 5 Tom4",
											   "Big Tom1",
											   "Big Tom2",
											   "Big Tom3",
											   "Big Tom4",			// 420
											   "Rock Tom1",
											   "Rock Tom2",
											   "Rock Tom3",
											   "Rock Tom4",
											   "Punch Tom1",
											   "Punch Tom2",
											   "Punch Tom3",
											   "Punch Tom4",
											   "Oak Tom1",
											   "Oak Tom2",			// 430
											   "Oak Tom3",
											   "Oak Tom4",
											   "Balsa Tom1",
											   "Balsa Tom2",
											   "Balsa Tom3",
											   "Balsa Tom4",
											   "Vintage Tom1",
											   "Vintage Tom2",
											   "Vintage Tom3",
											   "Vintage Tom4",		// 440
											   "Brush 1 Tom1",
											   "Brush 1 Tom2",
											   "Brush 1 Tom3",
											   "Brush 1 Tom4",
											   "Brush 2 Tom1",
											   "Brush 2 Tom2",
											   "Brush 2 Tom3",
											   "Brush 2 Tom4",
											   "Dark Tom1",
											   "Dark Tom2",		// 450
											   "Dark Tom3",
											   "Dark Tom4",
											   "Attack Tom1",
											   "Attack Tom2",
											   "Attack Tom3",
											   "Attack Tom4",
											   "Hall Tom1",
											   "Hall Tom2",
											   "Hall Tom3",
											   "Hall Tom4",		// 460
											   "Birch Tom1",
											   "Birch Tom2",
											   "Birch Tom3",
											   "Birch Tom4",
											   "Beechnut Tom1",
											   "Beechnut Tom2",
											   "Beechnut Tom3",
											   "Beechnut Tom4",
											   "Micro Tom1",
											   "Micro Tom2",		// 470
											   "Micro Tom3",
											   "Micro Tom4",
											   "Bend Tom1",
											   "Bend Tom2",
											   "Bend Tom3",
											   "Bend Tom4",
											   "Bowl Tom1",
											   "Bowl Tom2",
											   "Bowl Tom3",
											   "Bowl Tom4",		// 480
											   "Dirty Tom1",
											   "Dirty Tom2",
											   "Dirty Tom3",
											   "Dirty Tom4"
										   };
		#endregion
		#region tomGroup 485-560
		public static string[] tomGroup =		// 485 - 560
			{ 
				"Standard 1 Tom1",
				"Standard 1 Tom2",
				"Standard 1 Tom3",
				"Standard 1 Tom4",
				"Standard 1 Tom5",
				"Standard 1 Tom6",	// 490
				"Standard 2 Tom1",
				"Standard 2 Tom2",
				"Standard 2 Tom3",
				"Standard 2 Tom4",
				"Standard 2 Tom5",
				"Standard 2 Tom6",
				"Room 6 Tom1",
				"Room 6 Tom2",
				"Room 6 Tom3",
				"Room 6 Tom4",		// 500
				"Room 6 Tom5",
				"Room 6 Tom6",
				"Power Tom1",
				"Power Tom2",
				"Power Tom3",
				"Power Tom4",
				"Power Tom5",
				"Power Tom6",
				"Jazz 3 Tom1",
				"Jazz 3 Tom2",		// 510
				"Jazz 3 Tom3",
				"Jazz 3 Tom4",
				"Jazz 3 Tom5",
				"Jazz 3 Tom6",
				"Brush 3 Tom1",
				"Brush 3 Tom2",
				"Brush 3 Tom3",
				"Brush 3 Tom4",
				"Brush 3 Tom5",
				"Brush 3 Tom6",		// 520
				"Gate Tom1",
				"Gate Tom2",
				"Gate Tom3",
				"Gate Tom4",
				"LoFi Tom1",
				"LoFi Tom2",
				"LoFi Tom3",
				"LoFi Tom4",
				"Elec Bend Tom1",
				"Elec Bend Tom2",	// 530
				"Elec Bend Tom3",
				"Elec Bend Tom4",
				"Elec Bend 2 Tom1",
				"Elec Bend 2 Tom2",
				"Elec Bend 2 Tom3",
				"Elec Bend 2 Tom4",
				"Elec Bend 3 Tom1",
				"Elec Bend 3 Tom2",
				"Elec Bend 3 Tom3",
				"Elec Bend 3 Tom4",	// 540
				"Elec Noise Tom1",
				"Elec Noise Tom2",
				"Elec Noise Tom3",
				"Elec Noise Tom4",
				"Elec Dual Tom1",
				"Elec Dual Tom2",
				"Elec Dual Tom3",
				"Elec Dual Tom4",
				"Elec Tom1",
				"Elec Tom2",		// 550
				"Elec Tom3",
				"Elec Tom4",
				"Elec Tom5",
				"Elec Tom6",
				"TR808 Tom1",
				"TR808 Tom2",
				"TR808 Tom3",
				"TR808 Tom4",
				"TR808 Tom5",
				"TR808 Tom6"		// 560
			};	
		#endregion
		#region hihatGroup 561-598
		public static string[] hihatGroup =	// 561 - 598
			{ 
				"Pure Hihat",
				"PureEGHihat",
				"BrightHihat",
				"BriteGHihat",
				"Jazz Hihat",
				"HazzEGHihat",
				"Thin Hihat",
				"ThinEGHihat",
				"Heavy Hihat",
				"HevyEGHihat",		// 570
				"Light Hihat",
				"LigtEGHihat",
				"Dark Hihat",
				"DarkEGHihat",
				"12 Hihat",
				"12 EG Hihat",
				"13 Hihat",
				"13 EG Hihat",
				"14 Hihat",
				"14 EG Hihat",		// 580
				"15 Hihat",
				"15 EG Hihat",
				"Brush 1 Hihat",
				"Brush 2 Hihat",
				"Sizzle Hihat",
				"Sizzle 2 Hihat",
				"Voice Hihat",
				"HandC Hihat",
				"Tambarine Hihat",
				"Maracas Hihat",	// 590
				"TR808 Hihat",
				"TR909 Hihat",
				"CR78 Hihat",
				"Metal 808 Hihat",
				"Metal 909 Hihat",
				"Metal78 Hihat",
				"LoFi1 Hihat",
				"LoFi2 Hihat"
			};
		#endregion
		#region crashGroup 599-634
		public static string[] crashGroup =	// 599 - 634
			{ 
				"Medium 14 Crash",
				"Medium 16 Crash",	// 600
				"Mediuim 18 Crash",
				"Quick 16 Crash",
				"Quick 18 Crash",
				"Thin 16 Crash",
				"Thin 18 Crash",
				"Brush 1 Crash",
				"Brush 2 Crash",
				"Sizzle Brush Crash",
				"Swell Crash",
				"Splash 6",		// 610
				"Splash 8",
				"Splash 10",
				"Splash 12",
				"Cup 4",
				"Cup 6",
				"Hand Splash 8",
				"Hand Splash 10",
				"China 10",
				"China 12",
				"China 18",		// 620
				"China 20",
				"Sizzle China",
				"Swell China",
				"PGYZBACK",
				"Pgy Crash 1",
				"Pgy Crash 2",
				"Pgy Crash 3",
				"Pg Splash 1",
				"Pg Splash 2",
				"Phase Cymbal",	// 630
				"Elec Crash",
				"TR808 Crash",
				"LoFi 1 Crash",
				"LoFi 2 Crash"
			};
		#endregion
		#region rideGroup 635-679
		public static string[] rideGroup =		// 635 - 679
			{
				"Jazz Ride",
				"Jazz Ride Edge",
				"Jazz Ride Bell",
				"Jazz Ride X (edge/bell)",
				"Pop Ride",
				"Pop Ride Edge",	// 640
				"Pop Ride Bell",
				"Pop Ride X (edge/bell)",
				"Rock Ride",
				"Rock Ride Edge",
				"Rock Ride Bell",
				"Rock Ride X (edge/bell)",
				"Lite Ride",
				"Lite Ride Edge",
				"Lite Ride Bell",
				"Lite Ride X (edge/bell)",	//650
				"Crash Ride",
				"Crash Ride Edge",
				"Dark Crash Ride",
				"Dark Crash Ride Edge",
				"Brush 1 Ride",
				"Brush 2 Ride",
				"Sizzle Brush Ride",
				"Sizzle 1 Ride",
				"Sizzle 1 Ride Edge",
				"Sizzle 1 Ride Bell",		// 660
				"Sizzle 1 Ride X (edge/bell)",
				"Sizzle 2 Ride",
				"Sizzle 2 Ride Edge",
				"Sizzle 2 Ride Bell",
				"Sizzle 2 Ride X (edge/bell)",
				"Sizzle 3 Ride",
				"Sizzle 3 Ride Edge",
				"Sizzle 3 Ride Bell",
				"Sizzle 3 Ride X (edge/bell)",
				"Sizzle 4 Ride",		// 670
				"Pigmy Ride 1",
				"Pigmy Ride 1 Bell",
				"Pigmy Ride 1 X (edge/bell)",
				"Pigmy Ride 2",
				"Pigmy Ride 2 Bell",
				"Pigmy Ride 2 X (edge/bell)",
				"LoFi Ride",
				"LoFi Ride Edge",
				"LoFi Ride Bell"
			};
		#endregion
		#region percGroup 680-810
		public static string[] percGroup =		// 680 - 810
			{
				"R8 Bongo Hi",
				"R8 Bongo Lo",
				"R8 Bongo 2 Hi",
				"R8 Bongo 2 Lo",
				"Bongo Hi",
				"Bongo Lo",
				"Bongo 2 Hi",
				"Bongo 2 Lo",
				"R8 Conga Mt",
				"R8 Conga Hi",
				"R8 Conga Lo",		// 690
				"Conga Mt",
				"Conga Sl",
				"Conga Op",
				"Conga Lo",
				"Conga Mt Vs",
				"Conga Sl Vs",
				"Cowbell 1",
				"Cowbell 2",
				"Cowbell Duo",
				"Claves",		// 700
				"Guiro Long 1",
				"Guiro Short",
				"Guiro Long 2",
				"Guiro Vs",
				"Maracas",
				"Shaker",
				"Small Shaker",
				"Tambarine",
				"Tambarine 2",
				"Tambarine 3",		// 710
				"Tambarine 4",
				"Timbale Hi",
				"Timbale Rm",
				"Timbale Lo",
				"Paila",
				"Timbale 2 Hi",
				"Timbale 2 Lo",
				"Vibraslap",
				"Agogo Hi",
				"Agogo Lo",			// 720
				"Agogo 2 Hi",
				"Agogo 2 Lo",
				"Cabasa Up",
				"Cabasa Down",
				"Cabasa Vs",
				"Cuica Mt 1",
				"Cuica Up",
				"Cuica Lo",
				"Cuica Mt 2",
				"Pandro Mt",		// 730
				"Pandroop",
				"Pandro Sl",
				"Pandro Vs",
				"Surdoh Mt",
				"Surdoh P",
				"Surdoh Vs",
				"Surcol Mt",
				"Surdol Op",
				"Surdol Vs",
				"Whistle",		// 740
				"Whistle Sh",
				"Caxixi",
				"Tabla Na",
				"Tabla Tin",
				"Tabla Un",
				"Tabla Te",
				"Tabla Ti",
				"Baya Ge",
				"Baya Ka",
				"Baya Gin",		// 750
				"Baya Sld",
				"Pot Drum",
				"Pot Drum Mt",
				"Pot Drum Vs",
				"Talkin Drum",
				"Thai Gong",
				"Thai Gong 2",
				"Bell Tree",
				"Tiny Gong",
				"Gong",			// 760
				"Temple Bell",
				"Wa-Daiko",
				"Taiko",
				"Sleigh bell",
				"Tree Chime",
				"Triangle Op",
				"Triangle Mt",
				"Triangle Vs",
				"R70 Trio P",
				"R70 Tri Mt",		// 770
				"R70 Tri Vs",
				"Castanet",
				"Wood Block Hi",
				"Wood Block Lo",
				"Concert Bd",
				"Concert Bd Mt",
				"Hand Cymbal",
				"Hand Cymbal Mt",
				"Timpani G",
				"Timpani C",		// 780
				"Timpani E",
				"Perc Hit 1",
				"Perc Hit 2",
				"Orch Maj",
				"Orch Min",
				"Orch Dim",
				"Kick/roll",
				"Kick/Cymbal",
				"Orch Roll",
				"Orch Chok",		// 790
				"Hit Roll",
				"Finale",
				"808 Clap",
				"808 Cowbell 1",
				"808 Cowbell 2",
				"808 Maracas",
				"808 Claves",
				"808 Conga",
				"909 Rim",
				"909 Clap",		// 800
				"78 Cowbell",
				"78 Guiro",
				"78 Guiro St",
				"78 Maracas",
				"78 M Beat",
				"78 Tambarine",
				"78 Bongo",
				"78 Claves",
				"78 Rim",
				"55 Claves"		// 810
			};
		#endregion
		#region specialGroup 811-888
		public static string[] specialGroup =		// 811 - 888
			{ 
				"Applause",
				"Encore",
				"Bird",
				"Dog",
				"Bubbles",
				"Heart Beat",
				"Telephone",
				"Punch",
				"Kung Foo",
				"Pistol",		// 820
				"Gun Shot",
				"Glass",
				"Hammer",
				"Bucket",
				"Barrel",
				"Trashcan",
				"AF Stomp",
				"Bounce",
				"Cuica Hit",
				"Monster",		// 830
				"Airdrive",
				"Car Door",
				"Car Cell",
				"Car Engine",
				"Car Horn",
				"Helicopter",
				"Thunder",
				"Bomb",
				"Sticks",
				"Click",		// 840
				"Tamb FX",
				"TEK Click",
				"Beep Hi",
				"Beep Lo",
				"Metro Bell",
				"Metro Click",
				"Snaps",
				"Clap",
				"Noise Clap",
				"Tek Noise",	// 850
				"Metal Slap",
				"R8 Slap",
				"Vocoder 1",
				"Vocoder 2",
				"Vocoder 3",
				"Dyn Scratch",
				"Scratch 1",
				"Scratch 2",
				"Scratch 3",
				"Scratch 4",	// 860
				"Scratch 5",
				"Scratch 6",
				"Scratch Loop",
				"Phil Hit",
				"LoFi Hit",
				"Hi-Q",
				"Hoo...",
				"DAO Drill",
				"Scrape",
				"Martian",		// 870
				"CoroCoro",
				"Coro Bend",
				"Burt",
				"Boing 1",
				"Boing 2",
				"Tekno Bird",
				"Nantoka!",
				"Elec Bird",
				"Metal Bend 1",
				"Metal Bend 2",		// 880
				"Metal Noise",
				"Metal Phase",
				"Laser",
				"Mystery",
				"Time Trip",
				"Kick Amb",
				"Snare Amb",
				"Tom Amb"		// 888
			};
		#endregion
		#region melodicGroup 889-920
		public static string[] melodicGroup =		// 889 - 920
			{ 
				"Kalimba",
				"Steel Drum",		// 890
				"Glockenspeil",
				"Vibraphone",
				"Marimba",
				"Xylophone",
				"Tubular Bell",
				"Celesta",
				"Saw Wave",
				"Tb Bass",
				"Slap Bass",
				"Guitar Slide",		// 900
				"Guitar Scratch",
				"Guitar Dist",
				"Guitar Bs 1",
				"Guitar Bs 2",
				"Cut Guitar Down",
				"Cut Guitar Up",
				"Fret Noise",
				"Bass Slide",
				"Wah Guitar Down 1",
				"Wah Guitar Up 1",		// 910
				"Wah Guitar Down 2",
				"Wah Guitar Up 2",
				"Shami Vs",
				"Brass Vs",
				"Strings Vs",
				"Pizicato",
				"Tekno Hit",
				"Funk Hit 1",
				"Funk Hit 2",
				"Func Hit 3"			// 920
			};
		#endregion
		#region voiceGroup 921-971
		public static string[] voiceGroup =		// 921 - 971
			{ 
				"Lady Ahh",
				"Aoouu!",
				"Hooh!",
				"Haa!",
				"Say Yeah!",
				"Yeah",
				"Ahhh",
				"Haaa",
				"Achaa!",
				"Nope!",		// 930
				"Bap",
				"Dat",
				"BapDat Vs",
				"Doot",
				"DAO Fall 1",
				"DAO Fall 2",
				"DAO Fall 3",
				"DAO Fall 4",
				"DoDat Vs",
				"DoDao Vs",
				"Scat 1 Vs",
				"Scat 2 Vs",
				"Scat 3 Vs",
				"Scat 4 Vs",
				"Scat 5 Vs",
				"Voice K",
				"Voice Ok",
				"Voice S",
				"Voice T1",
				"Voice T2",
				"Voice T3",
				"Voice T4",
				"Voice Cr",
				"Count 1",
				"Count 2",
				"Count 3",
				"Count 4",
				"Count 5",
				"Count 6",
				"Count 7",
				"Count 8",
				"Count 9",
				"Count 10",
				"Count 11",
				"Count 12",
				"Count 13",
				"Count And",
				"Count E",
				"Count A",
				"Count Ti",
				"Count Ta"
			};
		#endregion
		#region reverseGroup 972-989
		public static string[] reverseGroup =		// 972 - 989
			{ 
				"Reverse Kick 1",
				"Reverse Kick 2",
				"Reverse Snare 1",
				"Reverse Snare 2",
				"Reverse Tom",
				"Reverse Crash 1",
				"Reverse Crash 2",
				"Reverse China",
				"Reverse Bell Tree",		// 980
				"Reverse Hi-Q",
				"Reverse MFaze",
				"Reverse Air Dr",
				"Reverse Boing 1",
				"Reverse Boing 2",
				"Reverse Bend",
				"Reverse Vocoder",
				"Reverse Carcl",
				"Reverse Car Engine"
			};
		#endregion
		#region fixedhhGroup 990-1023
		public static string[] fixedhhGroup =		// 990 - 1023
			{
				"Standard Closed HH",			// 990
				"Standard 1 Edge Closed HH",
				"Standard 1 Open HH",
				"Standard 1 Edge Open HH",
				"Standard 1 Pedal HH",
				"Standard 2 Closed HH",
				"Standard 2 Edge Closed HH",
				"Standard 2 Open HH",
				"Pedal HH",
				"Room Closed HH",
				"Room Edge Closed HH",			// 1000
				"Room Open HH",
				"Room Edge Open HH",
				"Room Pedal HH",
				"Power Closed HH",
				"Power Edge Closed HH",
				"Power Open HH",
				"Power Pedal HH",
				"Brush Closed HH",
				"Brush Edge Closed HH",
				"Brush Open HH",
				"Brush Pedal HH",
				"Elec Closed HH",
				"Elec Open HH",
				"Elec Pedal HH",
				"808 Closed HH",
				"808 Edge Closed HH",
				"808 Open HH",
				"808 Edge Open HH",
				"808 Pedal HH",
				"LoFi Closed HH",
				"LoFi Open HH",
				"LoFi Edge Open HH",
				"LoFi Pedal HH"
			};
		#endregion
		#region offGroup 1024-1024
		public static string[] offGroup = { "Off" };			// 1024
		#endregion
		#region instGroups
		public static string[] nodeNames =  
		{
			"V-Kick Group",
			"Kick Group",
			"V-Snare Drums",
			"Snare Drums",
			"V-Tom Group",
			"Tom Group",
			"Hi-Hat Group",
			"Crash Cymbal Group",
			"Ride Cymbal Group",
			"Percussion Group",
			"Special Group",
			"Melodic Group",
			"Voice Group",
			"Reverse Group",
			"Fixed Hi-Hat Group",
			"Off Group"
		};
		public static string[][] instGroupNames = new string[NumGroups][];
		public static string[] instrumentNames = new string[NumInstruments];
		#endregion
		#endregion

		#region member variables
		private MidiChannelMap			inputChannelMap = new MidiChannelMap();
		private MidiChannelMap			outputChannelMap = new MidiChannelMap();
		internal int					currentKitNum = 0;

		private IProjectMidiParent		projectMidiParent = null;
		private ISongSet				isongset;

		private IIProjectMidiCollection	iprojectmidis;

		private string					TD8TemplateSysExFilePath;
		private	string					TD8Folder;
		private string					currentFileName;

		private int						numKits;
		public TD8Kit[]					kits;

		private	bool					dirtyFlag = false;
		//private bool					templateDirtyFlag = false;

		private TD8Edit frm = null;

		[ProjectMidiEventAttribute("Inc/Dec",128,"SongNum",ConnectionType.Normal,"Send the program number whenever it changes.  This can be caused by the Inc/Dec buttons.")]
		public event ProjectMidiEventHandler OnIncDecDevInput;
		[ProjectMidiEventAttribute("Cowbell",128,"",ConnectionType.Normal,"Sent whenever the Cowbell is struck.")]			// No default
		public event ProjectMidiEventHandler OnCowBellDevInput;
		#endregion

		#region	Actions
		[ProjectMidiActionAttribute("CacheSongs",1,"CacheSongs","System calls this to sync song information.")]
		public void OnCacheSongs( object source, EventArgs e )
		{
			ObjectEventArgs eObj = e as ObjectEventArgs;
			Debug.Assert(eObj!=null,"TD8 OnCacheSongs error: e is not an ObjectEventArgs");
			if(eObj==null) return;
			ISongCollection isongs = eObj.obj as ISongCollection;
			Debug.Assert(isongs!=null,"TD8 OnCacheSongs error: e is not an ISongCollection");
			Console.WriteLine("TD8 OnCacheSongs, # songs = {0}",isongs.Count);
			//Debug.Assert(ie.data < 64,"TD8 OnNumSongsChanged error: # songs too big: "+ie.data);
			int songnum = 0;
			foreach(ISong song in isongs)
			{
				int pagenum = 0;
				foreach(IPage page in song.Pages)
				{
					SetOrCachePatch(song.Name,songnum,pagenum,0);
					pagenum++;
					IPage donothing = page;	// Eliminate warning message, that's all
				}
				songnum++;
			}
		}
		[ProjectMidiActionAttribute("Song#",128,"SongNum","Selects the song number from the current set.  TD8 patches for that song will be loaded.")]
		void OnSongNumChanged( object sender, EventArgs e )
		{
			IntEventArgs ie = e as IntEventArgs;
			Debug.Assert(ie!=null,"TD8 OnSongNumChanged error: e is not an IntEventArgs");
			if(ie==null) return;
			Debug.Assert(ie.data < isongset.Songs.Count,"TD8: OnSongNumChanged error.  Song # too big: "+ie.data);
			SetOrCachePatch(isongset.Songs.Item(ie.data).Name,ie.data,isongset.CurrentPageNum,isongset.CurrentPatchNum);
			currentFileName = Path.Combine(TD8Folder,Path.GetFileNameWithoutExtension(isongset.Songs.Item(ie.data).Name)+".TD8");
			currentKitNum = ie.data;

			Console.WriteLine("SetPatch writing program change {0}",currentKitNum);
			MidiOut( new MidiData( (byte)(MidiStatus.ProgramChange+DevMidiChan),(byte)currentKitNum,0),0);
		}
		[ProjectMidiActionAttribute("Page#",128,"PageNum","Selects the page number for the current song.  TD8 patches for that page will be loaded.")]
		void OnPageNumChanged( object sender, EventArgs e )
		{
			IntEventArgs ie = e as IntEventArgs;
			Debug.Assert(ie!=null,"TD8 OnPageNumChanged error: e is not an IntEventArgs");

		}
		[ProjectMidiActionAttribute("Patch#",128,"PatchNum","Selects the patch number for the current page.  TD8 patches for that patch number will be loaded.")]
		void OnPatchNumChanged( object sender, EventArgs e )
		{
			IntEventArgs ie = e as IntEventArgs;
			Debug.Assert(ie!=null,"TD8 OnPatchNumChanged error: e is not an IntEventArgs");

		}
		#endregion

		#region Properties
		public bool DirtyFlag
		{
			get { return dirtyFlag; }
			set { dirtyFlag = value; }
		}
		#endregion

		#region TD8 Constructor
		public TD8()
		{
		}
		#endregion

		#region ProjectMidiParent
		// This will happen immediately after construction
		public IProjectMidiParent ProjectMidiParent 
		{
			set 
			{ 
				projectMidiParent = value; 
				iprojectmidis = projectMidiParent.IProjectMidis;
				//projectMidiParent.AllAssembliesLoadedEvent +=new ProjectMidiEventHandler(AllAssembliesLoadedHandler);

				#region fill instGroupNames
				instGroupNames[0] = vkickGroup;
				instGroupNames[1] = kickGroup;
				instGroupNames[2] = vsnareGroup;
				instGroupNames[3] = snareGroup;
				instGroupNames[4] = vtomGroup;
				instGroupNames[5] = tomGroup;
				instGroupNames[6] = hihatGroup;
				instGroupNames[7] = crashGroup;
				instGroupNames[8] = rideGroup;
				instGroupNames[9] = percGroup;
				instGroupNames[10] = specialGroup;
				instGroupNames[11] = melodicGroup;
				instGroupNames[12] = voiceGroup;
				instGroupNames[13] = reverseGroup;
				instGroupNames[14] = fixedhhGroup;
				instGroupNames[15] = offGroup;
				#endregion

				// Fill the instrumentNames array
				int x = 0;
				foreach(string[] astr in instGroupNames)
				{
					foreach(string str in astr)
					{
						instrumentNames[x++] = str;
					}
				}

				// Get our path to our individual TD-8 files
				TD8Folder = Path.Combine(projectMidiParent.ProjectMidiPath,TD8FolderName);
				TD8TemplateSysExFilePath = Path.Combine(TD8Folder,"Default.TD8");
				if(!Directory.Exists(TD8Folder)) Directory.CreateDirectory( TD8Folder );
				LoadAllKitsFromSysExFile();		// TODO: this may need to get moved to the DeviceDetected section(s)
			}
		}

		#endregion
		#region PerformStartProcessing
		public void PerformStartProcessing( StartPhase phase )
		{
			switch(phase)
			{
				case StartPhase.AfterLoading:
					break;
				case StartPhase.AllAssembliesLoaded:
					AllAssembliesLoaded();
					break;
				case StartPhase.StartConnecting:
					break;
				case StartPhase.FirstTimeEver:
					CreateOurConnections();
					break;
				case StartPhase.AllAssembliesConnected:
					break;
				case StartPhase.Terminating:
					break;
				default:
					Console.WriteLine("Unknown StartPhase");
					break;
			}
		}
		#endregion

		#region CreateOurConnections
		private void CreateOurConnections()
		{
			if(projectMidiParent==null) Console.WriteLine("TD8 ConnectOurConnections Error: projectMidiParent not set!");
			else 
			{
				// PDF Ink submenu
				projectMidiParent.CreateAConnection("Menu", "PDFContextMenu",
					"TD8", "Editor", "System", "-1",
					"True", "False", "TD8 Editor");
				// Icon Menu
				projectMidiParent.CreateAConnection("Menu", "PMIconMenu",
					"TD8", "Editor", "System", "-1",
					"True", "False", "TD8 Editor");
				// Midi In
				projectMidiParent.CreateAConnection("Midi", "AnyIn",
					"TD8", "MidiIn", "System", "-1",
					"True", "False", null);
				// Midi Out
				projectMidiParent.CreateAConnection("TD8", "MidiOut",
					"Midi", "AnyOut", "System", "-1",
					"True", "False", null);
				// Inc/Dec
				projectMidiParent.CreateAConnection("TD8", "Inc/Dec",
					"SongSet", "Song#", "System", "-1",
					"True", "False", null);
			}
		}
		#endregion

		#region All Assemblies Loaded Handler
		// Locate the ISongSet after all modules have been loaded
		private void AllAssembliesLoaded()
		{
			Debug.Assert(iprojectmidis!=null,"TD8 AllAssembliesLoadedHandler error: iprojectmidis is null");
			foreach(IProjectMidi iproj in iprojectmidis)
			{
				isongset = iproj as ISongSet;
				if(isongset!=null) break;
			}
		}
		#endregion

		#region IProjectMidiHideShow Members
		[ProjectMidiActionAttribute("Hide/Show",0,"","Hide or Show TD8 Editor window.")]
		public void OnHideShow(object sender, EventArgs e) { HideShow(); }
		public void HideShow()
		{
			if(frm == null)
			{
				frm = new TD8Edit();
				frm.Td8 = this;

				frm.Show();
				//dirtyFlag = true;		// Assume changes were made.
				//Console.WriteLine("TD8: Saving changes");
				//SavePatch();
				//dirtyFlag = false;
			}
			else
			{
				frm.Close();
				frm = null;
			}
			//this.Visible = !this.Visible;
		}
		#endregion

		#region LoadAllKitsFromSysExFile
		// Read all data from a SysEx dump file.
		// This is used to provide 'template' kits
		public void LoadAllKitsFromSysExFile()
		{
			Console.WriteLine("LoadAllBanksFromSysExFile()");
			// Eventually, this should be loaded via SysEx from the TD-8 device itself.
			// For now, this will be read from a file.  Prompt for the file.
//			RegistryKey key = projectMidiParent.ProjectMidiKey; //Registry.LocalMachine.CreateSubKey(RegistryPath);
//			TD8Folder = (string)key.GetValue( TD8TemplateSysExFilePathKey );
//			if(!File.Exists(TD8TemplateSysExFilePath))
//			{
//				OpenFileDialog dlg = new OpenFileDialog();
//				dlg.Filter = "SysEx dump files (*.syx)|*.syx";
//				dlg.Title = "Select TD-8 Patch File";
//				if(dlg.ShowDialog()!=DialogResult.OK)
//				{
//					MessageBox.Show("Error loading TD8 SysEx file");
//					return;
//				}
//				TD8TemplateSysExFilePath = dlg.FileName;
//				key.SetValue( TD8TemplateSysExFilePathKey, TD8TemplateSysExFilePath );
//			}

			// Create an instance of StreamReader to read from a file.
			// The using statement also closes the StreamReader.
			if(!File.Exists(TD8TemplateSysExFilePath))
			{
				MessageBox.Show("TD8 file ("+TD8TemplateSysExFilePath+") not found.");
				return;
			}
			using(FileStream fs = new FileStream(TD8TemplateSysExFilePath, FileMode.Open, FileAccess.Read))
			{
				numKits = (int)(fs.Length / 697);
				if(numKits < 1)					
				{
					Console.WriteLine("File is the wrong size ({0}).  It must be at least 697 bytes.",fs.Length);
					MessageBox.Show("File is the wrong size.  It must be at least 697 bytes.");
					numKits = 1;
					kits = new TD8Kit[numKits]; 
					return;
				}
				kits = new TD8Kit[numKits]; 
				for( int drumKit = 0; drumKit < numKits; drumKit++)
				{
					kits[drumKit] = new TD8Kit();
					LoadKitFromOpenSysExFile(kits[drumKit], drumKit, fs);
				}
				dirtyFlag = false;
			}
		}
		#endregion
		#region LoadKitFromOpenSysExFile
		// Read data for a single kit from an open file.
		public void LoadKitFromOpenSysExFile(TD8Kit kit, int kitid, FileStream fs)
		{
TraceMessage("TD8: LoadKitFromOpenSysExFile");
			//Console.WriteLine("LoadBankFromOpenSysExFile");
			// Read 'Common' parameters (size 25 + 10 + 2)
			int nBytes=NumSysExCommonBytes;
			byte[] ByteArray=new byte[nBytes];
			int nBytesRead=fs.Read(ByteArray, 0, nBytes);
			if(nBytesRead != nBytes)
			{
				Console.WriteLine("Error: bytes read = {0} of expected {1}",nBytesRead,nBytes);
				MessageBox.Show("Error 1 reading file.");
				return;
			}
			LoadKitCommonFromBuffer(kit, kitid, ByteArray, nBytesRead);

			// Read each pad's parameters (12)
			for(int padNum = 0; padNum < TD8Kit.NumPads; padNum++)
			{
				// Read 'Pad' parameters (size 43 + 10 + 2)
				nBytes=NumSysExPadBytes;
				ByteArray=new byte[nBytes];
				nBytesRead=fs.Read(ByteArray, 0, nBytes);
				if(nBytesRead != nBytes)
				{
					Console.WriteLine("LoadKitFromOpenSysExFile Error: bytes read = {0} of expected {1}",nBytesRead,nBytes);
					MessageBox.Show("Error 2 reading file.");
					return;
				}
				LoadKitPadFromBuffer(kit, padNum, ByteArray, nBytesRead);
			}
		}
		#endregion
		#region LoadKitCommonFromBuffer
		private void LoadKitCommonFromBuffer(TD8Kit kit, int kitid, byte[] data, int dsize)
		{
			//Console.WriteLine("LoadKitCommonFromBuffer");
			// Read 'Common' parameters (size 25 + 10 + 2)
			int nBytes=NumSysExCommonBytes;
			if(nBytes < dsize)
			{
				Console.WriteLine("LoadKitCommonFromBuffer Error: dsize too small {0}",dsize);
				MessageBox.Show("Error 3 reading file.");
				return;
			}
			string name = "";
			for(int i = SysExPrefixLen; i<SysExPrefixLen+8; i++) name += (char)data[i];
			kit.Common.Name = name;
			kit.Common.StudioType = (TD8Common.enumStudioType)data[SysExPrefixLen+8];
			kit.Common.StudioLevel = data[SysExPrefixLen+9];
			kit.Common.WallType = (TD8Common.enumWallType)data[SysExPrefixLen+10];
			kit.Common.RoomSize = (TD8Common.enumRoomSize)data[SysExPrefixLen+11];
			kit.Common.EQLowFreq = (TD8Common.enumEQLowFreq)data[SysExPrefixLen+12];
			kit.Common.EQLowGain = data[SysExPrefixLen+13];
			kit.Common.EQHighFreq = (TD8Common.enumEQHighFreq)data[SysExPrefixLen+14];
			kit.Common.EQHighGain = data[SysExPrefixLen+15];
			kit.Common.AmbienceOnOff = data[SysExPrefixLen+16]!=0;
			kit.Common.EQOnOff = data[SysExPrefixLen+17]!=0;
			kit.Common.BrushOnOff = data[SysExPrefixLen+18]!=0;
			kit.Common.PedalHiHatVolume = data[SysExPrefixLen+19];
			kit.Common.PedalBendRange = data[SysExPrefixLen+20];
			kit.Common.MasterVolume = data[SysExPrefixLen+21];
			kit.Common.AmbienceGroupKitSendLevel = data[SysExPrefixLen+22];
			kit.Common.AmbienceGroupPercSendLevel = data[SysExPrefixLen+23];
			kit.Common.AmbienceGroupPartSendLevel = data[SysExPrefixLen+24];
		}
		#endregion
		#region LoadKitPadFromBuffer
		private void LoadKitPadFromBuffer(TD8Kit kit, int padid, byte[] data, int dsize)
		{
			//Console.WriteLine("LoadKitPadFromBuffer");
			// Read 'Pad' parameters (size 43 + 10 + 2)
			if(dsize < NumSysExPadBytes)
			{
				Console.WriteLine("LoadKitPadFromBuffer Error: size {0} too small ({1})",dsize,NumSysExPadBytes);
				MessageBox.Show("Error 4 reading file.");
				return;
			}
			// Head
			uint inst = (uint)(data[SysExPrefixLen]<<12);		// nibbled
			inst += (uint)(data[SysExPrefixLen+1]<<8);
			inst += (uint)(data[SysExPrefixLen+2]<<4);
			inst += data[SysExPrefixLen+3];
			if(inst > 1024)
			{
				Console.WriteLine("LoadKitPadFromBuffer error: head inst > 1024 ({0})",inst);
				inst = 0;
			}
			kit.Pads[padid].Pad[0].Instrument = inst;				// 0-1024	nibbled
			int pitch = data[SysExPrefixLen+4]<<12;
			pitch += data[SysExPrefixLen+5]<<8;
			pitch += data[SysExPrefixLen+6]<<4;
			pitch += data[SysExPrefixLen+7];
			if(pitch > 960)
			{
				Console.WriteLine("LoadKitPadFromBuffer error: head pitch > 960 ({0})",pitch);
				pitch = 480;
			}
			kit.Pads[padid].Pad[0].Pitch = pitch;					// 0-960 (-4800 - +4800 cent, 10 cent steps) nibbled
			kit.Pads[padid].Pad[0].Decay = data[SysExPrefixLen+8];
			uint ptrn = (uint)(data[SysExPrefixLen+9]<<12);
			ptrn += (uint)(data[SysExPrefixLen+10]<<8);
			ptrn += (uint)(data[SysExPrefixLen+11]<<4);
			ptrn += data[SysExPrefixLen+12];
			if(ptrn > 800)
			{
				Console.WriteLine("LoadKitPadFromBuffer error: head pattern > 800 ({0})",pitch);
				pitch = 0;
			}
			kit.Pads[padid].Pad[0].PlayPatternNumber = ptrn;		// 0-800 (0=off)	nibbled
			kit.Pads[padid].Pad[0].GateTime = data[SysExPrefixLen+13];
			kit.Pads[padid].Pad[0].NoteNum = data[SysExPrefixLen+14];
			kit.Pads[padid].Pad[0].PadPatternVelocity = data[SysExPrefixLen+15] != 0;
			kit.Pads[padid].Pad[0].Level = data[SysExPrefixLen+16];
			kit.Pads[padid].Pad[0].AmbienceSendLevel = data[SysExPrefixLen+17];
			kit.Pads[padid].Pad[0].PitchCtrlAssign = data[SysExPrefixLen+18] != 0;
			// Rim
			inst = (uint)(data[SysExPrefixLen+19]<<12);		// nibbled
			inst += (uint)(data[SysExPrefixLen+20]<<8);
			inst += (uint)(data[SysExPrefixLen+21]<<4);
			inst += data[SysExPrefixLen+22];
			if(inst > 1024)
			{
				Console.WriteLine("LoadKitPadFromBuffer error: rim inst > 1024 ({0})",inst);
				inst = 0;
			}
			kit.Pads[padid].Pad[1].Instrument = inst;	// 0-1024 (nibbled)
			pitch = data[SysExPrefixLen+23]<<12;
			pitch += data[SysExPrefixLen+24]<<8;
			pitch += data[SysExPrefixLen+25]<<4;
			pitch += data[SysExPrefixLen+26];
			if(pitch > 960)
			{
				Console.WriteLine("LoadKitPadFromBuffer error: rim pitch > 960 ({0})",pitch);
				pitch = 480;
			}
			kit.Pads[padid].Pad[1].Pitch = pitch;	// 0-960 (-4800 - +4800 cent, 10 cent steps)
			kit.Pads[padid].Pad[1].Decay = data[SysExPrefixLen+27];
			ptrn = (uint)(data[SysExPrefixLen+28]<<12);
			ptrn += (uint)(data[SysExPrefixLen+29]<<8);
			ptrn += (uint)(data[SysExPrefixLen+30]<<4);
			ptrn += (uint)(data[SysExPrefixLen+31]);
			if(ptrn > 800)
			{
				Console.WriteLine("LoadKitPadFromBuffer error: rim pattern > 800 ({0})",pitch);
				pitch = 0;
			}
			kit.Pads[padid].Pad[1].PlayPatternNumber = ptrn;		// 0-800 (0=off)
			kit.Pads[padid].Pad[1].GateTime = data[SysExPrefixLen+32];
			kit.Pads[padid].Pad[1].NoteNum = data[SysExPrefixLen+33];
			kit.Pads[padid].Pad[1].PadPatternVelocity = data[SysExPrefixLen+34] != 0;
			kit.Pads[padid].Pad[1].Level = data[SysExPrefixLen+35];
			kit.Pads[padid].Pad[1].AmbienceSendLevel = data[SysExPrefixLen+36];
			kit.Pads[padid].Pad[1].PitchCtrlAssign = data[SysExPrefixLen+37] != 0;
			kit.Pads[padid].Pan = data[SysExPrefixLen+38];
			kit.Pads[padid].ShellDepth = (TD8Pad.enumShellDepth)(data[SysExPrefixLen+39]);
			kit.Pads[padid].HeadType = (TD8Pad.enumHeadType)(data[SysExPrefixLen+40]);
			kit.Pads[padid].Muffling = (TD8Pad.enumMuffling)(data[SysExPrefixLen+41]);
			kit.Pads[padid].StrainerAdjustment = (TD8Pad.enumStrainerAdjustment)(data[SysExPrefixLen+42]);
		}
		#endregion
		#region WriteAllKitsToSysExFile
		// Write all data to a SysEx dump file.
		public void WriteAllKitsToSysExFile()
		{
			//Console.WriteLine("WriteAllBanksToSysExFile()");
			// Create an instance of StreamReader to write to a file.
			// The using statement also closes the StreamReader.
			using(FileStream fs = new FileStream(TD8TemplateSysExFilePath, FileMode.Create, FileAccess.Write))
			{
				if(numKits < 1)					
				{
					Console.WriteLine("File is the wrong size ({0}).  It must be at least 697 bytes.",fs.Length);
					MessageBox.Show("WriteAllBanksToSysEx Error: numKits < 1.");
					// Go ahead and correct the problem.
					numKits = 1;	
					kits = new TD8Kit[numKits]; 
					return;
				}
				for( int drumKit = 0; drumKit < numKits; drumKit++)
				{
					WriteKitToSysExFile(kits[drumKit], (byte)drumKit, fs);
				}
			}
		}
		#endregion
		#region WriteKitToSysExFile
		// Write all data to a SysEx dump file.
		public void WriteKitToSysExFile(TD8Kit kit, byte kitid, FileStream fs)
		{
			//Console.WriteLine("WriteKitToSysExFile()");
			// Write 'Common' parameters (size 25 + 10 + 2)
			int nBytes=NumSysExCommonBytes;
			byte[] data=new byte[nBytes];
			WriteKitCommonToBuffer(kit, kitid, data, nBytes);

			try 
			{
				fs.Write(data, 0, nBytes);
			}
			catch( Exception e3 )
			{
				Console.WriteLine("Error writing common data: {0}",e3.Message);
				MessageBox.Show("Error 1 writing file.");
				return;
			}

			// Write each pad's parameters (12)
			for(int padNum = 0; padNum < TD8Kit.NumPads; padNum++)
			{
				// Write 'Pad' parameters (size 43 + 10 + 2)
				nBytes=NumSysExPadBytes;
				data=new byte[nBytes];
				WriteKitPadToBuffer(kit, kitid, padNum, data, NumSysExPadBytes);
				
				try 
				{
					fs.Write(data, 0, nBytes);
				}
				catch( Exception e3 )
				{
					Console.WriteLine("Error writing pad data: {0}",e3.Message);
					MessageBox.Show("Error 2 writing file.");
					return;
				}
			}
		}
		#endregion
		#region WriteKitToBuffer
		// Write all data to a data buffer.
		public void WriteKitToBuffer(TD8Kit kit, byte kitid, byte[] data, int bufsize)
		{
			//Console.WriteLine("WriteKitToBuffer()");
			if(bufsize < 697)
			{
				Console.WriteLine("WriteKitToBuffer Error: bufsize too small.");
				return;
			}
			WriteKitCommonToBuffer(kit, kitid, data, bufsize);

			// Write each pad's parameters (12)
			for(int padNum = 0; padNum < TD8Kit.NumPads; padNum++)
			{
				int offset = NumSysExCommonBytes+(padNum*NumSysExPadBytes);
				byte[] tdata = new byte[NumSysExPadBytes];
				WriteKitPadToBuffer(kit, kitid, padNum, tdata, NumSysExPadBytes);
				for(int i=0; i<NumSysExPadBytes; i++) data[offset+i]=tdata[i];

			}
		}
		#endregion
		#region WriteKitCommonToBuffer
		// Write common section data to a data buffer.
		public void WriteKitCommonToBuffer(TD8Kit kit, byte kitid, byte[] data, int bufsize)
		{
			//Console.WriteLine("WriteKitCommonToBuffer()");
			if(bufsize < NumSysExCommonBytes)
			{
				Console.WriteLine("WriteKitCommonToBuffer Error: bufsize too small.");
				return;
			}
			// Write 'Common' parameters (size 25 + 10 + 2)
			data[0] = 0xf0;
			data[1] = 0x41;
			data[2] = DevNum;			// dev;
			data[3] = 0x00;
			data[4] = 0x20;
			data[5] = 0x12;
			data[6] = 0x41;				// addMSB;
			data[7] = kitid;			// add2;
			data[8] = 0;				// add3;
			data[9] = 0;				// addLSB;
			for(int x=0; x<8; x++)
			{
				byte b = (byte)(kit.Common.Name[x]);
				data[x+SysExPrefixLen] = b;
			}
			data[18] = (byte)kit.Common.StudioType;
			data[19] = (byte)kit.Common.StudioLevel;
			data[20] = (byte)kit.Common.WallType;
			data[21] = (byte)kit.Common.RoomSize;
			data[22] = (byte)kit.Common.EQLowFreq;
			data[23] = (byte)kit.Common.EQLowGain;
			data[24] = (byte)kit.Common.EQHighFreq;
			data[25] = (byte)kit.Common.EQHighGain;
			data[26] = (byte)(kit.Common.AmbienceOnOff ? 1 : 0);
			data[27] = (byte)(kit.Common.EQOnOff ? 1 : 0);
			data[28] = (byte)(kit.Common.BrushOnOff ? 1 : 0);
			data[29] = (byte)kit.Common.PedalHiHatVolume;
			data[30] = (byte)kit.Common.PedalBendRange;
			data[31] = (byte)kit.Common.MasterVolume;
			data[32] = (byte)kit.Common.AmbienceGroupKitSendLevel;
			data[33] = (byte)kit.Common.AmbienceGroupPercSendLevel;
			data[34] = (byte)kit.Common.AmbienceGroupPartSendLevel;

			byte chksum = 0;
			for(int x=6; x<35; x++)
			{
				chksum += data[x];
			}
			chksum = (byte)(chksum & 0x7f);
			if(chksum > 0) chksum = (byte)(0x80 - (chksum & 0x7f));
			data[35] = chksum;
			data[36] = EOX;
		}
		#endregion
		#region WriteKitPadToBuffer
		// Write 1 pad's data to a data buffer.
		public void WriteKitPadToBuffer(TD8Kit kit, byte kitid, int padNum, byte[] data, int bufsize)
		{
			//Console.WriteLine("WriteKitPadToBuffer()");
			if(bufsize < NumSysExPadBytes)
			{
				Console.WriteLine("WriteKitPadToBuffer Error: bufsize too small.");
				return;
			}
			// Write 'Pad' parameters (size 43 + 10 + 2)
			data[0] = 0xf0;
			data[1] = 0x41;
			data[2] = DevNum;			// dev;
			data[3] = 0x00;
			data[4] = 0x20;
			data[5] = 0x12;
			data[6] = 0x41;				// addMSB;
			data[7] = kitid;			// add2;
			data[8] = (byte)(1+padNum);	// add3;
			data[9] = 0;				// addLSB;
			// Head (use .Pad[0])
			uint inst = kit.Pads[padNum].Pad[0].Instrument;
			if(inst > 1023)
			{
				Console.WriteLine("Invalid inst # {0} pad {1}",inst,padNum);
				inst = 0;
			}
			data[10] = (byte)((inst>>12) & 0x0f);	// nibbled
			data[11] = (byte)((inst>>8) & 0x0f);
			data[12] = (byte)((inst>>4) & 0x0f);
			data[13] = (byte)(inst & 0x0f);
			uint pitch = (uint)(kit.Pads[padNum].Pad[0].Pitch);	// 0-960 (-4800 - +4800 cent, 10 cent steps)
			if(pitch > 960)
			{
				Console.WriteLine("Invalid pitch # {0} pad {1}",pitch,padNum);
				pitch = 480;
			}
			data[14] = (byte)((pitch>>12) & 0x0f);	// nibbled
			data[15] = (byte)((pitch>>8) & 0x0f);
			data[16] = (byte)((pitch>>4) & 0x0f);
			data[17] = (byte)(pitch & 0x0f);
			data[18] = (byte)kit.Pads[padNum].Pad[0].Decay;
			uint ptrn = (uint)(kit.Pads[padNum].Pad[0].PlayPatternNumber);	// 0-800 (0=off)
			if(ptrn > 800)
			{
				Console.WriteLine("Invalid pattern # {0} pad {1}",ptrn,padNum);
				ptrn = 0;
			}
			data[19] = (byte)((ptrn>>12) & 0x0f);	// nibbled
			data[20] = (byte)((ptrn>>8) & 0x0f);
			data[21] = (byte)((ptrn>>4) & 0x0f);
			data[22] = (byte)(ptrn & 0x0f);
			data[23] = (byte)kit.Pads[padNum].Pad[0].GateTime;
			data[24] = (byte)kit.Pads[padNum].Pad[0].NoteNum;
			data[25] = (byte)(kit.Pads[padNum].Pad[0].PadPatternVelocity ? 1 : 0);
			data[26] = (byte)kit.Pads[padNum].Pad[0].Level;
			data[27] = (byte)kit.Pads[padNum].Pad[0].AmbienceSendLevel;
			data[28] = (byte)(kit.Pads[padNum].Pad[0].PitchCtrlAssign ? 1 : 0);
			// Rim (use .Pad[1])
			inst = kit.Pads[padNum].Pad[1].Instrument;
			if(inst > 1023)
			{
				Console.WriteLine("Invalid inst # {0} pad {1}",inst,padNum);
				inst = 0;
			}
			data[29] = (byte)((inst>>12) & 0x0f);		// nibbled
			data[30] = (byte)((inst>>8) & 0x0f);
			data[31] = (byte)((inst>>4) & 0x0f);
			data[32] = (byte)(inst & 0x0f);
			pitch = (uint)(kit.Pads[padNum].Pad[1].Pitch);	// 0-960 (-4800 - +4800 cent, 10 cent steps)
			if(pitch > 960)
			{
				Console.WriteLine("Invalid pitch # {0} pad {1}",pitch,padNum);
				pitch = 480;
			}
			data[33] = (byte)((pitch>>12) & 0x0f);		// nibbled
			data[34] = (byte)((pitch>>8) & 0x0f);
			data[35] = (byte)((pitch>>4) & 0x0f);
			data[36] = (byte)(pitch & 0x0f);
			data[37] = (byte)kit.Pads[padNum].Pad[1].Decay;
			ptrn = (uint)(kit.Pads[padNum].Pad[1].PlayPatternNumber);	// 0-800 (0=off)
			if(ptrn > 800)
			{
				Console.WriteLine("Invalid pattern # {0} pad {1}",ptrn,padNum);
				ptrn = 0;
			}
			data[38] = (byte)((ptrn>>12) & 0x0f);	// nibbled
			data[39] = (byte)((ptrn>>8) & 0x0f);
			data[40] = (byte)((ptrn>>4) & 0x0f);
			data[41] = (byte)(ptrn & 0x0f);
			data[42] = (byte)kit.Pads[padNum].Pad[1].GateTime;
			data[43] = (byte)kit.Pads[padNum].Pad[1].NoteNum;
			data[44] = (byte)(kit.Pads[padNum].Pad[1].PadPatternVelocity ? 1 : 0);
			data[45] = (byte)kit.Pads[padNum].Pad[1].Level;
			data[46] = (byte)kit.Pads[padNum].Pad[1].AmbienceSendLevel;
			data[47] = (byte)(kit.Pads[padNum].Pad[1].PitchCtrlAssign ? 1 : 0);
			data[48] = (byte)kit.Pads[padNum].Pan;
			data[49] = (byte)kit.Pads[padNum].ShellDepth;
			data[50] = (byte)kit.Pads[padNum].HeadType;
			data[51] = (byte)kit.Pads[padNum].Muffling;
			data[52] = (byte)kit.Pads[padNum].StrainerAdjustment;

			byte chksum = 0;
			for(int x=6; x<53; x++)
			{
				chksum += data[x];
			}
			chksum = (byte)(chksum & 0x7f);
			if(chksum > 0) chksum = (byte)(0x80 - (chksum & 0x7f));
			data[53] = chksum;
			data[54] = EOX;
		}
		#endregion

		#region MidiInData
		[ProjectMidiActionAttribute("MidiIn",1,"AnyMidiIn","Connected this to the desired MID-In port.")]
		void OnMidiInData( object sender, EventArgs e )
		{
			ObjectEventArgs oe = e as ObjectEventArgs;
			if(oe==null) return;
			IMidiData mididata = oe.obj as IMidiData;
			if(mididata==null) return;

			//if(mididata.IsActiveSense || mididata.IsTimingClock) activeSense = true;

			// Fire any actionss associated with this particular event
			if(mididata.IsNoteOn)
			{
				switch( mididata.KeyNum )					
				{
//					case BassDrumNoteNum:
//						BassDrumDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case SnareDrumNoteNum:
//						SnareDrumDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case SnareDrumRimNoteNum:
//						SnareRimDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case HighHatNoteNum:
//						HighHatDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case HighTomNoteNum:
//						HighTomDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case MidTomNoteNum:
//						MidTomDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case LowTomNoteNum:
//						LowTomDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case FloorTomNoteNum:
//						FloorTomDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case CrashCymbalNoteNum:
//						CrashCymbalDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case SplashCymbalNoteNum:
//						SplashCymbalDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
//					case RideCymbalNoteNum:
//						RideCymbalDevInput.Actions.FireActions( Name, mididata.Byte2 );
//						break;
					case CowbellNoteNum:
						if(OnCowBellDevInput!=null) OnCowBellDevInput( this, new EventArgs() );
						break;
				}
			}
			else if(mididata.IsProgramChange)
			{
				if(OnIncDecDevInput!=null) OnIncDecDevInput( this, new IntEventArgs( mididata.ProgramNum) );
			}

			// Calculate tempo based on Snare, Bass and HH inputs
			if( mididata.IsNoteOn )
			{
				switch(mididata.KeyNum)
				{
					case BassDrumNoteNum:

						break;
					case SnareDrumNoteNum:

						break;
					case HighHatNoteNum:

						break;
				}
			}
		}
		#endregion

		#region Select Patch Dialog
		// SELECT PATCH DIALOG
		[ProjectMidiActionAttribute("Editor",1,"","This causes the TD8 voice selection dialog to be displayed.")]
		private void SelectPatchDialog( object sender, EventArgs e )
		{
			Console.WriteLine("SelectPatchDialog()");
			if(frm == null)
			{
				frm = new TD8Edit();
				frm.Td8 = this;

				frm.Show();
				//dirtyFlag = true;		// Assume changes were made.
				//Console.WriteLine("TD8: Saving changes");
				//SavePatch();
				//dirtyFlag = false;
			}
		}
		#endregion

		#region Set or Cache Patch
		// SET OR CACHE PATCH
		private bool SetOrCachePatch(string songFileName,int songListIndex,int pageNum,int patchNum)
		{
			Console.WriteLine("SetOrCachePatch: {0}, {1}, {2}, {3}",songFileName,songListIndex,pageNum,patchNum);
TraceMessage("TD8: SetOrCachePatch: "+songFileName+", page "+pageNum.ToString());

			if(currentKitNum == songListIndex && dirtyFlag == true)	// Do we need to save this kit?
			{
				Console.WriteLine("TD8: Saving previous patch");
				SavePatch();
				dirtyFlag = false;
			}
TraceMessage("TD8: SongListIndex="+songListIndex.ToString());
			if(kits[songListIndex].Common.Name == Path.GetFileNameWithoutExtension(songFileName))
			{
				Console.WriteLine("TD8: Patch {0} already cached: {1}.",songListIndex,songFileName);
TraceMessage("TD8: Patch already cached");
				return true;
			}
TraceMessage("TD8: Patch not already cached");
TraceMessage("TD8: folder = "+TD8Folder);
			string patchFileName = Path.Combine(TD8Folder,Path.GetFileNameWithoutExtension(songFileName)+".TD8");
TraceMessage("Patch file name = "+patchFileName);
			// Load the file if it exists and hasn't already been loaded
			if( File.Exists( patchFileName ))
			{
				Console.WriteLine("TD8: Loading patch file");
TraceMessage("TD8: Loading patch file");
				using(FileStream fs = new FileStream(patchFileName, FileMode.Open, FileAccess.Read))
				{
					if(fs.Length < 697)
					{
						Console.WriteLine("File is the wrong size ({0}).  It must be at least 697 bytes.",fs.Length);
						MessageBox.Show("File is the wrong size.  It must be at least 697 bytes.");
						return false;
					}
					LoadKitFromOpenSysExFile(kits[songListIndex], songListIndex, fs);
				}
			}
			else	
			{
TraceMessage("File not found.  Copying default kit.");
				Console.WriteLine("TD8: Copying default kit");
				// Otherwise, copy the default template and give it the song name
				CopyKit( kits[songListIndex], kits[DefaultKitNum] );
				kits[songListIndex].Common.Name = Path.GetFileNameWithoutExtension(songFileName);
			}

			//Console.WriteLine("SetPatch writing program change {0}",currentKitNum);
			//int rc = outputPort.Write((byte)(MidiStatus.ProgramChange+DevMidiChan),(byte)currentKitNum,0);
			// Now write the patch to the TD-8
TraceMessage("Sending kit");
			Console.WriteLine("TD8: Sending kit {0} to TD8",songListIndex);
			SendKitToTD8( songListIndex );
			Thread.Sleep(40);

			return true;
		}
		#endregion

		#region Save Patch
		// SAVE PATCH
		internal int SavePatch()
		{
			Console.WriteLine("SavePatch()");
			int rc = 0;
			if(dirtyFlag == true)	// Do we need to save this kit?
			{
				// Is this the default filename?
				if(currentFileName == Path.Combine(TD8Folder,"Default.TD8"))
				{
					SaveFileDialog dlg = new SaveFileDialog();
					dlg.Filter = "SysEx dump files (*.td8)|*.td8";
					dlg.Title = "Select TD-8 Patch File";
					dlg.InitialDirectory = TD8Folder;
					if(dlg.ShowDialog()==DialogResult.OK)
					{
						currentFileName = dlg.FileName;
					}
				}
				try
				{
					// Save the current kit (unless it is default?)
					using(FileStream fs = new FileStream(currentFileName, FileMode.Create, FileAccess.Write))
					{
						WriteKitToSysExFile(kits[currentKitNum], (byte)currentKitNum, fs);
					}
				}
				catch( Exception e )
				{
					Console.WriteLine("Error: {0}",e.Message);
					rc = 4;
				}
				dirtyFlag = false;
			}
			return rc;
		}
		#endregion

		#region Send Kit to TD8
		// SEND KIT TO TD8
		public int SendKitCommonToTD8(int patchID)
		{
			int rc = 0;
			//Console.WriteLine("SendKitCommonToTD8()");
			// Write 'Common' parameters (size 25 + 10 + 2)
			int nBytes=NumSysExCommonBytes;
			byte[] data=new byte[nBytes];
			WriteKitCommonToBuffer(kits[patchID], (byte)patchID, data, nBytes);
			SysExMessage message = new SysExMessage(SysExType.Start, data);
			MidiOut(message,0);
			Thread.Sleep(15);
			return rc;
		}
		public int SendKitPadToTD8(int patchID, int padid)
		{
			int rc = 0;
			//Console.WriteLine("SendKitPadToTD8()");
			int nBytes=NumSysExPadBytes;
			byte[] data=new byte[nBytes];
			WriteKitPadToBuffer(kits[patchID], (byte)patchID, padid, data, nBytes);
			SysExMessage message = new SysExMessage(SysExType.Start, data);
			MidiOut(message,0);
			Thread.Sleep(15);
			return rc;
		}
		public int SendKitToTD8(int patchID)
		{
			int rc = 0;
			SendKitCommonToTD8(patchID);
			// Write out all 12 pads
			for(int p=0; p<12; p++)
			{
				SendKitPadToTD8(patchID, p);
			}
			return rc;
		}
		// SEND a Byte to the TD8
		public int SendByteParameterToTD8(byte b, byte a1, byte a2, byte a3, byte a4)
		{
			//Console.WriteLine("SendByteParameterToTD8 {0}, {1}",b,(a1<<12)+(a2<<8)+(a3<<4)+a4);
			int rc = 0;
			byte[] data = new byte[13];
			data[0] = 0xf0;
			data[1] = 0x41;
			data[2] = DevNum;		// dev;
			data[3] = 0x00;
			data[4] = 0x20;
			data[5] = 0x12;
			data[6] = a1;			// addMSB;
			data[7] = a2;			// add2;
			data[8] = a3;			// add3;
			data[9] = a4;			// addLSB;
			data[10] = b;

			byte chksum = 0;
			for(int x=6; x<11; x++)
			{
				chksum += data[x];
			}
			chksum = (byte)(chksum & 0x7f);
			if(chksum > 0) chksum = (byte)(0x80 - (chksum & 0x7f));
			data[11] = chksum;
			data[12] = EOX;
			SysExMessage message = new SysExMessage(SysExType.Start, data);
			MidiOut(message,0);
			Thread.Sleep(15);
			return rc;
		}
		public int SendNibbledParameterToTD8(uint parm, byte a1, byte a2, byte a3, byte a4)
		{
			int rc = 0;
			//Console.WriteLine("SendNibbledParameterToTD8 {0}, {1}",b,(a1<<12)+(a2<<8)+(a3<<4)+a4);
			byte[] data = new byte[16];
			data[0] = 0xf0;
			data[1] = 0x41;
			data[2] = DevNum;		// dev;
			data[3] = 0x00;
			data[4] = 0x20;
			data[5] = 0x12;
			data[6] = a1;			// addMSB;
			data[7] = a2;			// add2;
			data[8] = a3;			// add3;
			data[9] = a4;			// addLSB;
			data[10] = (byte)((parm>>12) & 0x0f);	// nibbled
			data[11] = (byte)((parm>>8) & 0x0f);
			data[12] = (byte)((parm>>4) & 0x0f);
			data[13] = (byte)(parm & 0x0f);

			byte chksum = 0;
			for(int x=6; x<14; x++)
			{
				chksum += data[x];
			}
			chksum = (byte)(chksum & 0x7f);
			if(chksum > 0) chksum = (byte)(0x80 - (chksum & 0x7f));
			data[14] = chksum;
			data[15] = EOX;
			SysExMessage message = new SysExMessage(SysExType.Start, data);
			MidiOut(message,0);
			Thread.Sleep(15);
			return rc;
		}
		#endregion

		#region Copy Kit
		// Copy every kit member except name
		public void CopyKit(TD8Kit dest, TD8Kit src)
		{
TraceMessage("CopyKit");
			Console.WriteLine("CopyKit()");
			try
			{
				//dest.Common.Name = src.Common.Name;
				dest.Common.StudioType = src.Common.StudioType;
				dest.Common.StudioLevel = src.Common.StudioLevel;
				dest.Common.WallType = src.Common.WallType;
				dest.Common.RoomSize = src.Common.RoomSize;
				dest.Common.EQLowFreq = src.Common.EQLowFreq;
				dest.Common.EQLowGain = src.Common.EQLowGain;
				dest.Common.EQHighFreq = src.Common.EQHighFreq;
				dest.Common.EQHighGain = src.Common.EQHighGain;
				dest.Common.AmbienceOnOff = src.Common.AmbienceOnOff;
				dest.Common.EQOnOff = src.Common.EQOnOff;
				dest.Common.BrushOnOff = src.Common.BrushOnOff;
				dest.Common.PedalHiHatVolume = src.Common.PedalHiHatVolume;
				dest.Common.PedalBendRange = src.Common.PedalBendRange;
				dest.Common.MasterVolume = src.Common.MasterVolume;
				dest.Common.AmbienceGroupKitSendLevel = src.Common.AmbienceGroupKitSendLevel;
				dest.Common.AmbienceGroupPercSendLevel = src.Common.AmbienceGroupPercSendLevel;
				dest.Common.AmbienceGroupPartSendLevel = src.Common.AmbienceGroupPartSendLevel;
				// Write each pad's parameters (12)
				for(int padNum = 0; padNum < TD8Kit.NumPads; padNum++)
				{
					dest.Pads[padNum].HeadType = src.Pads[padNum].HeadType;
					dest.Pads[padNum].Muffling = src.Pads[padNum].Muffling;
					dest.Pads[padNum].Pan = src.Pads[padNum].Pan;
					dest.Pads[padNum].ShellDepth = src.Pads[padNum].ShellDepth;
					dest.Pads[padNum].StrainerAdjustment = src.Pads[padNum].StrainerAdjustment;
					for(int x=0; x<2; x++)
					{
						dest.Pads[padNum].Pad[x].AmbienceSendLevel = src.Pads[padNum].Pad[x].AmbienceSendLevel;
						dest.Pads[padNum].Pad[x].Decay = src.Pads[padNum].Pad[x].Decay;
						dest.Pads[padNum].Pad[x].GateTime = src.Pads[padNum].Pad[x].GateTime;
						dest.Pads[padNum].Pad[x].Instrument = src.Pads[padNum].Pad[x].Instrument;
						dest.Pads[padNum].Pad[x].Level = src.Pads[padNum].Pad[x].Level;
						dest.Pads[padNum].Pad[x].NoteNum = src.Pads[padNum].Pad[x].NoteNum;
						dest.Pads[padNum].Pad[x].PadPatternVelocity = src.Pads[padNum].Pad[x].PadPatternVelocity;
						dest.Pads[padNum].Pad[x].Pitch = src.Pads[padNum].Pad[x].Pitch;
						dest.Pads[padNum].Pad[x].PitchCtrlAssign = src.Pads[padNum].Pad[x].PitchCtrlAssign;
						dest.Pads[padNum].Pad[x].PlayPatternNumber = src.Pads[padNum].Pad[x].PlayPatternNumber;
					}
				}
			}
			catch(Exception e)
			{
TraceMessage("Error in CopyKit: "+e.Message);
				Console.WriteLine("TD8 CopyKit error: {0}",e.Message);
			}
		}
		#endregion

		#region OnMidiOut Event
		// This is our out-going MIDI Event.  We use it to pass messages
		// to the MIDI port, or any other midi listeners.
		[ProjectMidiEventAttribute("MidiOut",1,"AnyMidiOut",ConnectionType.Normal,"Connect this to the MIDI Port that the TD8 is attached to.")]
		public event ProjectMidiEventHandler OnMidiOut;
		public void MidiOut( MidiData mdata, uint timestamp )
		{
			try
			{
				// Pass the string msg in a StringEventArgs (derived from EventArgs)
				ObjectEventArgs e = new ObjectEventArgs( mdata );

				// Fire the event (if connected).
				if(OnMidiOut!=null) OnMidiOut( this, e );

				// Display a copy also in our output window during debugging.
				Console.WriteLine("TD8 MidiOut: {0}",mdata.ToString());
			}
			catch(Exception err)
			{
				Console.WriteLine("TD8 MidiOut error: {0}",err.Message);
			}
		}
		public void MidiOut( SysExMessage message, uint timestamp )
		{
			try
			{
				// Pass the string msg in a StringEventArgs (derived from EventArgs)
				ObjectEventArgs e = new ObjectEventArgs( message );

				// Fire the event (if connected).
				if(OnMidiOut!=null) OnMidiOut( this, e );

				// Display a copy also in our output window during debugging.
				Console.WriteLine("TD8 MidiOut: SysEx");
			}
			catch(Exception err)
			{
				Console.WriteLine("TD8 MidiOut SysEx error: {0}",err.Message);
			}
		}
		#endregion

		#region Trace
		[ProjectMidiEventAttribute("Trace",1,"TraceMessage",ConnectionType.Trace,"Sends TD8 diagnostic trace messages.")]
		public event ProjectMidiEventHandler OnTraceMessage;
		public void TraceMessage( string msg )
		{
			try
			{
				// Pass the string msg in a StringEventArgs (derived from EventArgs)
				StringEventArgs e = new StringEventArgs( msg );

				// Fire the event (if connected).
				if(OnTraceMessage!=null) OnTraceMessage( this, e );

				// Display a copy also in our output window during debugging.
				Console.WriteLine("SampleAssembly TraceMessage: {0}",msg);
			}
			catch(Exception err)
			{
				Console.WriteLine("SampleAssembly TraceMessage error: {0}",err.Message);
			}
		}
		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
Ron is a senior software engineer.
His hobbies include riding motorcycles, travel, and scuba diving.

He enjoys learning about science, particularly quantum physics and cosmology.

He is active with his church where he plays drums and keyboards with the contemporary church band each week.
He also designed and maintains his church and band websites (http://TheRockUMC.org and http://TheRockBand.org).

Comments and Discussions