/// <summary>
        /// Starts a voice with the given key and velocity.
        /// </summary>
        /// <param name="channel">The midi channel this voice is on.</param>
        /// <param name="note">The key the voice will play in.</param>
        /// <param name="velocity">The volume of the voice.</param>
        public void NoteOn(int channel, int note, int velocity)
        {
            // Get the correct instrument depending if it is a drum or not
            Patch inst = bank.GetPatch(bankSelect[channel], programs[channel]);

            if (inst == null)
            {
                return;
            }
            // A NoteOn can trigger multiple voices via layers
            List <Patch> layers = new List <Patch>();

            if (inst is MultiPatch)
            {
                ((MultiPatch)inst).FindPatches(channel, note, velocity, layers);
                if (layers.Count == 0)
                {
                    return;
                }
            }
            else
            {
                layers.Add(inst);
            }
            // If a key with the same note value exists, stop it
            if (voiceManager.registry[channel, note] != null)
            {
                VoiceManager.VoiceNode node = voiceManager.registry[channel, note];
                while (node != null)
                {
                    node.Value.Stop();
                    node = node.Next;
                }
                voiceManager.RemoveFromRegistry(channel, note);
            }
            // Check exclusive groups
            for (int x = 0; x < layers.Count; x++)
            {
                bool notseen = true;
                for (int i = x - 1; i >= 0; i--)
                {
                    if (layers[x].ExclusiveGroupTarget == layers[i].ExclusiveGroupTarget)
                    {
                        notseen = false;
                        break;
                    }
                }
                if (layers[x].ExclusiveGroupTarget != 0 && notseen)
                {
                    LinkedListNode <Voice> node = voiceManager.activeVoices.First;
                    while (node != null)
                    {
                        if (layers[x].ExclusiveGroupTarget == node.Value.Patch.ExclusiveGroup)
                        {
                            node.Value.Stop();
                            voiceManager.RemoveFromRegistry(node.Value);
                        }
                        node = node.Next;
                    }
                }
            }
            // Assign a voice to each layer
            for (int x = 0; x < layers.Count; x++)
            {
                Voice voice = voiceManager.GetFreeVoice();
                voice.Configure(channel, note, velocity, layers[x]);
                voiceManager.AddToRegistry(voice);
                voiceManager.activeVoices.AddLast(voice);
                voice.Start();
            }
        }
        /// <summary>
        /// Starts a voice with the given key and velocity.
        /// </summary>
        /// <param name="channel">The midi channel this voice is on.</param>
        /// <param name="note">The key the voice will play in.</param>
        /// <param name="velocity">The volume of the voice.</param>
        public void NoteOn(int channel, int note, int velocity)
        {
            // Get the correct instrument depending if it is a drum or not
            SynthParameters sChan = synthChannels[channel];
            Patch           inst  = bank.GetPatch(sChan.bankSelect, sChan.program);

            if (inst == null)
            {
                return;
            }
            // A NoteOn can trigger multiple voices via layers
            int layerCount;

            if (inst is MultiPatch)
            {
                layerCount = ((MultiPatch)inst).FindPatches(channel, note, velocity, layerList);
            }
            else
            {
                layerCount   = 1;
                layerList[0] = inst;
            }
            // If a key with the same note value exists, stop it
            if (voiceManager.registry[channel, note] != null)
            {
                VoiceManager.VoiceNode node = voiceManager.registry[channel, note];
                while (node != null)
                {
                    node.Value.Stop();
                    node = node.Next;
                }
                voiceManager.RemoveFromRegistry(channel, note);
            }
            // Check exclusive groups
            for (int x = 0; x < layerCount; x++)
            {
                bool notseen = true;
                for (int i = x - 1; i >= 0; i--)
                {
                    if (layerList[x].ExclusiveGroupTarget == layerList[i].ExclusiveGroupTarget)
                    {
                        notseen = false;
                        break;
                    }
                }
                if (layerList[x].ExclusiveGroupTarget != 0 && notseen)
                {
                    LinkedListNode <Voice> node = voiceManager.activeVoices.First;
                    while (node != null)
                    {
                        if (layerList[x].ExclusiveGroupTarget == node.Value.Patch.ExclusiveGroup)
                        {
                            node.Value.Stop();
                            voiceManager.RemoveFromRegistry(node.Value);
                        }
                        node = node.Next;
                    }
                }
            }
            // Assign a voice to each layer
            for (int x = 0; x < layerCount; x++)
            {
                Voice voice = voiceManager.GetFreeVoice();
                if (voice == null)// out of voices and skipping is enabled
                {
                    break;
                }
                voice.Configure(channel, note, velocity, layerList[x], synthChannels[channel]);
                voiceManager.AddToRegistry(voice);
                voiceManager.activeVoices.AddLast(voice);
                voice.Start();
            }
            // Clear layer list
            for (int x = 0; x < layerCount; x++)
            {
                layerList[x] = null;
            }
        }