diff --git a/trunk/src/engines/EngineChannelBase.h b/trunk/src/engines/EngineChannelBase.h
index 406d286..62a58c9 100644
--- a/trunk/src/engines/EngineChannelBase.h
+++ b/trunk/src/engines/EngineChannelBase.h
@@ -428,6 +428,105 @@ namespace LinuxSampler {
 
             template<class TV, class TRR, class TR, class TD, class TIM, class TI> friend class EngineBase;
 
+            /**
+             * Handle key group (a.k.a. exclusive group) conflicts.
+             * @param SelfMask       - Without self-masking notes turn off notes with the same pitch
+             *                         regardless of velocity. With self-masking higher-velocity notes
+             *                         turn off lower-velocity notes, but lower-velocity notes
+             *                         do not turn off higher-velocity notes.
+             *                         The case of notes with the same velocity is undocumented.
+             * @param NotePolyphony  - Maximum number of voices playing simultaneously triggered by the same key.
+             * @param GroupPolyphony - Maximum number of voices playing simultaneously within the same group.
+             * @param UseOffBy       - Set true only for sfz to make use of it's different key group concept.
+             * @param NewAlgorithm   - TODO: remove this argument if it makes into the trunk.
+             */
+            void HandleKeyGroupConflicts(int KeyGroup, Pool<Event>::Iterator& itNoteOnEvent, bool SelfMask = false, int NotePolyphony = 1, int GroupPolyphony = 1, bool UseOffBy = false, bool NewAlgorithm = false) {
+                dmsg(4,("EngineChannelBase::HandleKeyGroupConflicts KeyGroup=%d\n", KeyGroup));
+                if (NewAlgorithm) {
+                // Gather all active voices that might be released
+                // because of a group conflict, ordered by trigger time
+                typedef std::multimap<sched_time_t, typename MidiKeyboardManager<V>::RTListVoiceIterator> VoiceMap;
+                VoiceMap OrderedVoices;
+
+                // iterate through all active keys
+                for (Pool<uint>::Iterator iuiKey = this->pActiveKeys->first();
+                     iuiKey != this->pActiveKeys->end();
+                     ++iuiKey)
+                {
+                    MidiKey* pKey = &this->pMIDIKeyInfo[*iuiKey];
+                    // and active notes for each key
+                    for (typename MidiKeyboardManager<V>::RTListNoteIterator itNote = pKey->pActiveNotes->first();
+                         itNote != pKey->pActiveNotes->end();
+                         ++itNote)
+                    {
+                        // and active voices for each note
+                        for (typename MidiKeyboardManager<V>::RTListVoiceIterator itVoice = itNote->pActiveVoices->first();
+                             itVoice != itNote->pActiveVoices->end();
+                             ++itVoice)
+                        {
+                            // Add voice to the multimap if it's in the same group
+                            // (to check for NotePolyphony and GroupPolyphony)
+                            // or if it should be stopped by off_by opcode (sfz)
+                            if (itVoice->GetKeyGroup(false) == KeyGroup || itVoice->GetKeyGroup(true) == KeyGroup)
+                            {
+                                OrderedVoices.insert(typename VoiceMap::value_type(
+                                    itVoice->pNote->triggerSchedTime,
+                                    itVoice
+                                ));
+                            }
+                        }
+                    }
+                }
+
+                int NoteCount = 0;
+                int GroupCount = 0;
+
+                // Iterate in reverse order to save most recent voices and 
+                // release those triggered earlier
+                for (typename VoiceMap::reverse_iterator itMap = OrderedVoices.rbegin();
+                     itMap != OrderedVoices.rend();
+                     ++itMap)
+                {
+                    typename MidiKeyboardManager<V>::RTListVoiceIterator itVoice = itMap->second;
+                    bool releaseByNote  = false;
+                    bool releaseByGroup = false;
+
+                    // NotePolyphony check
+                    if (itNoteOnEvent->Param.Note.Key == itVoice->MIDIKey()) {
+                        ++NoteCount;
+
+                        releaseByNote = (
+                            (NotePolyphony > 0 && NoteCount >= NotePolyphony) && (
+                                !SelfMask || (
+                                    SelfMask &&
+                                    itNoteOnEvent->Param.Note.Velocity >= itVoice->MIDIVelocity()
+                                )
+                            )
+                        );
+                    }
+
+                    // GroupPolyphony check
+                    if (itVoice->GetKeyGroup(false) == KeyGroup) {
+                        ++GroupCount;
+                        releaseByGroup = (GroupPolyphony > -1 && GroupCount >= GroupPolyphony);
+                    }
+
+                    // Forced release
+                    bool releaseByOffBy = UseOffBy && itVoice->GetKeyGroup(true) == KeyGroup;
+
+                    ActiveKeyGroupMap::iterator it = ActiveKeyGroups.find(itVoice->GetKeyGroup(true));
+                    if (it != ActiveKeyGroups.end() && (releaseByNote || releaseByGroup || releaseByOffBy))
+                    {
+                        RTList<Event>::Iterator itEvent = it->second->allocAppend(pEngine->pEventPool);
+                        *itEvent = *itNoteOnEvent;
+                        itEvent->Type = Event::type_release_voice;
+                        itEvent->Param.ReleaseVoice.VoiceID = dynamic_cast<NotePool<V>*>(pEngine)->GetVoicePool()->getID(itVoice);
+                    }
+                }
+                // !NewAlgorithm
+                } else AbstractEngineChannel::HandleKeyGroupConflicts(KeyGroup, itNoteOnEvent);
+            }
+
         protected:
             EngineChannelBase() :
                 MidiKeyboardManager<V>(this),
diff --git a/trunk/src/engines/common/AbstractVoice.cpp b/trunk/src/engines/common/AbstractVoice.cpp
index 7be4e33..7114313 100644
--- a/trunk/src/engines/common/AbstractVoice.cpp
+++ b/trunk/src/engines/common/AbstractVoice.cpp
@@ -118,6 +118,7 @@ namespace LinuxSampler {
         itKillEvent     = Pool<Event>::Iterator();
         MidiKeyBase* pKeyInfo = GetMidiKeyInfo(MIDIKey());
 
+        // FIXME: in sfz iKeyGroup (off_by) = 0 is valid
         pGroupEvents = iKeyGroup ? pEngineChannel->ActiveKeyGroups[iKeyGroup] : 0;
 
         SmplInfo   = GetSampleInfo();
diff --git a/trunk/src/engines/common/AbstractVoice.h b/trunk/src/engines/common/AbstractVoice.h
index fe6b208..6841bee 100644
--- a/trunk/src/engines/common/AbstractVoice.h
+++ b/trunk/src/engines/common/AbstractVoice.h
@@ -124,6 +124,10 @@ namespace LinuxSampler {
             /// MIDI note-on velocity value which the voice should use for calculating any synthesis relevant parameters (i.e. amplitude).
             inline uint8_t MIDIVelocity() const { return pNote->cause.Param.Note.Velocity; }
 
+            // mute argument is relevant only for sfz interface,
+            // because of different key grouping concept
+            virtual int GetKeyGroup(bool mute = false) = 0;
+
             void processCCEvents(RTList<Event>::Iterator& itEvent, uint End);
             void processPitchEvent(RTList<Event>::Iterator& itEvent);
             void processResonanceEvent(RTList<Event>::Iterator& itEvent);
diff --git a/trunk/src/engines/common/Event.h b/trunk/src/engines/common/Event.h
index 1f97e21..49e5cba 100644
--- a/trunk/src/engines/common/Event.h
+++ b/trunk/src/engines/common/Event.h
@@ -134,6 +134,11 @@ namespace LinuxSampler {
     typedef pool_element_id_t note_id_t;
 
     /**
+     * Unique numeric ID of a voice.
+     */
+    typedef pool_element_id_t voice_id_t;
+
+    /**
      * Unique numeric ID of a script callback ID instance which can be used to
      * retrieve access to the actual @c ScriptEvent object. Once the script
      * callback instance associated with a certain ID stopped its execution
@@ -169,6 +174,7 @@ namespace LinuxSampler {
                 type_stop_note, ///< caused by a call to built-in instrument script function note_off()
                 type_kill_note, ///< caused by a call to built-in instrument script function fade_out()
                 type_note_synth_param, ///< change a note's synthesis parameters (upon real-time instrument script function calls, i.e. change_vol(), change_tune(), change_pan(), etc.)
+                type_release_voice,
             } Type;
             enum synth_param_t {
                 synth_param_volume,
@@ -244,6 +250,9 @@ namespace LinuxSampler {
                     bool          Relative; ///< Whether @c Delta should be applied relatively against the note's current synthesis parameter value (false means the paramter's current value is simply replaced by Delta).
                     float         AbsValue; ///< New current absolute value of synthesis parameter (that is after @c Delta being applied).
                 } NoteSynthParam;
+                struct _ReleaseVoice {
+                    voice_id_t VoiceID;
+                } ReleaseVoice;
             } Param;
             EngineChannel* pEngineChannel; ///< Pointer to the EngineChannel where this event occured on, NULL means Engine global event (e.g. SysEx message).
             MidiInputPort* pMidiInputPort; ///< Pointer to the MIDI input port on which this event occured (NOTE: currently only for global events, that is SysEx messages)
diff --git a/trunk/src/engines/gig/Engine.cpp b/trunk/src/engines/gig/Engine.cpp
index 588b3d1..a8e325b 100644
--- a/trunk/src/engines/gig/Engine.cpp
+++ b/trunk/src/engines/gig/Engine.cpp
@@ -226,11 +226,6 @@ namespace LinuxSampler { namespace gig {
         // if nothing defined for this key
         if (!pRegion) return Pool<Voice>::Iterator(); // nothing to do
 
-        int iKeyGroup = pRegion->KeyGroup;
-        // only need to send a group event from the first voice in a layered region,
-        // as all layers in a region always belongs to the same key group
-        if (HandleKeyGroupConflicts && iLayer == 0) pChannel->HandleKeyGroupConflicts(iKeyGroup, itNoteOnEvent);
-
         Voice::type_t VoiceType = Voice::type_normal;
 
         // get current dimension values to select the right dimension region
@@ -364,6 +359,11 @@ namespace LinuxSampler { namespace gig {
         }
         if (!pDimRgn) return Pool<Voice>::Iterator(); // error (could not resolve dimension region)
 
+        int iKeyGroup = pRegion->KeyGroup;
+        // only need to send a group event from the first voice in a layered region,
+        // as all layers in a region always belongs to the same key group
+        if (HandleKeyGroupConflicts && iLayer == 0) pChannel->HandleKeyGroupConflicts(iKeyGroup, itNoteOnEvent, pDimRgn->SelfMask);
+
         // no need to continue if sample is silent
         if (!pDimRgn->pSample || !pDimRgn->pSample->SamplesTotal) return Pool<Voice>::Iterator();
         
diff --git a/trunk/src/engines/gig/Voice.cpp b/trunk/src/engines/gig/Voice.cpp
index c9ec41b..e9fa227 100644
--- a/trunk/src/engines/gig/Voice.cpp
+++ b/trunk/src/engines/gig/Voice.cpp
@@ -510,6 +510,14 @@ namespace LinuxSampler { namespace gig {
     void Voice::ProcessGroupEvent(RTList<Event>::Iterator& itEvent) {
         dmsg(4,("Voice %p processGroupEvents event type=%d", (void*)this, itEvent->Type));
 
+        bool ReleaseVoice = (
+            itEvent->Type == Event::type_release_voice &&
+            itEvent->Param.ReleaseVoice.VoiceID == pEngine->GetVoicePool()->getID(this)
+        );
+        bool NewNote = (
+            itEvent->Type == Event::type_note_on &&
+            itEvent->Type == itEvent->Param.Note.Key != HostKey()
+        );
         // TODO: The SustainPedal condition could be wrong, maybe the
         // check should be if this Voice is in release stage or is a
         // release sample instead. Need to test this in GSt.
@@ -519,7 +527,7 @@ namespace LinuxSampler { namespace gig {
         // note should be stopped at all, because it doesn't sound naturally
         // with a drumkit.
         // -- Christian, 2013-01-08
-        if (itEvent->Param.Note.Key != HostKey() /*||
+        if (NewNote || ReleaseVoice /*||
             !GetGigEngineChannel()->SustainPedal*/) {
             dmsg(4,("Voice %p - kill", (void*)this));
 
diff --git a/trunk/src/engines/gig/Voice.h b/trunk/src/engines/gig/Voice.h
index a794358..89ed711 100644
--- a/trunk/src/engines/gig/Voice.h
+++ b/trunk/src/engines/gig/Voice.h
@@ -63,6 +63,7 @@ namespace LinuxSampler { namespace gig {
             void SetEngine(LinuxSampler::Engine* pEngine);
             void CalculateFadeOutCoeff(float FadeOutTime, float SampleRate);
             virtual release_trigger_t GetReleaseTriggerFlags() OVERRIDE;
+            virtual int               GetKeyGroup(bool mute = false) OVERRIDE { return pRegion->GetParent()->KeyGroup; }
 
         protected:
             virtual SampleInfo       GetSampleInfo() OVERRIDE;
diff --git a/trunk/src/engines/sf2/Voice.cpp b/trunk/src/engines/sf2/Voice.cpp
index 32b1847..63069ef 100644
--- a/trunk/src/engines/sf2/Voice.cpp
+++ b/trunk/src/engines/sf2/Voice.cpp
@@ -343,7 +343,15 @@ namespace LinuxSampler { namespace sf2 {
     }
 
     void Voice::ProcessGroupEvent(RTList<Event>::Iterator& itEvent) {
-        if (itEvent->Param.Note.Key != HostKey()) {
+        bool ReleaseVoice = (
+            itEvent->Type == Event::type_release_voice &&
+            itEvent->Param.ReleaseVoice.VoiceID == pEngine->GetVoicePool()->getID(this)
+        );
+        bool NewNote = (
+            itEvent->Type == Event::type_note_on &&
+            itEvent->Type == itEvent->Param.Note.Key != HostKey()
+        );
+        if (NewNote || ReleaseVoice) {
             // kill the voice fast
             SignalRack.EnterFadeOutStage();
         }
diff --git a/trunk/src/engines/sf2/Voice.h b/trunk/src/engines/sf2/Voice.h
index c902d23..e69f438 100644
--- a/trunk/src/engines/sf2/Voice.h
+++ b/trunk/src/engines/sf2/Voice.h
@@ -56,6 +56,7 @@ namespace LinuxSampler { namespace sf2 {
             void SetEngine(LinuxSampler::Engine* pEngine);
             void CalculateFadeOutCoeff(float FadeOutTime, float SampleRate);
             virtual release_trigger_t GetReleaseTriggerFlags() OVERRIDE;
+            virtual int               GetKeyGroup(bool mute = false) OVERRIDE { return pRegion->exclusiveClass; }
 
         protected:
             virtual SampleInfo       GetSampleInfo() OVERRIDE;
diff --git a/trunk/src/engines/sfz/Engine.cpp b/trunk/src/engines/sfz/Engine.cpp
index 1177503..f29b541 100644
--- a/trunk/src/engines/sfz/Engine.cpp
+++ b/trunk/src/engines/sfz/Engine.cpp
@@ -273,7 +273,10 @@ namespace LinuxSampler { namespace sfz {
 
         Pool<Voice>::Iterator itNewVoice;
 
-        if (HandleKeyGroupConflicts) pChannel->HandleKeyGroupConflicts(pRgn->group, itNoteOnEvent);
+        // TODO: check for discrepancy while loading Regions to the Instrument.
+        // Voices in the same group must have identical values for
+        // off_by, polyphony, note_polyphony and note_selfmask opcodes.
+        if (HandleKeyGroupConflicts) pChannel->HandleKeyGroupConflicts(pRgn->group, itNoteOnEvent, (bool)pRgn->note_selfmask, pRgn->note_polyphony, pRgn->polyphony, true, true);
 
         // no need to process if sample is silent
         if (!pRgn->GetSample(false) || !pRgn->GetSample()->GetTotalFrameCount()) return Pool<Voice>::Iterator();
diff --git a/trunk/src/engines/sfz/EngineChannel.cpp b/trunk/src/engines/sfz/EngineChannel.cpp
index 8f312e3..5d109d8 100644
--- a/trunk/src/engines/sfz/EngineChannel.cpp
+++ b/trunk/src/engines/sfz/EngineChannel.cpp
@@ -163,7 +163,6 @@ namespace LinuxSampler { namespace sfz {
         // rebuild ActiveKeyGroups map with key groups of current instrument
         for (std::vector< ::sfz::Region*>::iterator itRegion = newInstrument->regions.begin() ;
              itRegion != newInstrument->regions.end() ; ++itRegion) {
-            AddGroup((*itRegion)->group);
             AddGroup((*itRegion)->off_by);
         }
 
diff --git a/trunk/src/engines/sfz/Voice.cpp b/trunk/src/engines/sfz/Voice.cpp
index b2f2d5a..3ea1087 100644
--- a/trunk/src/engines/sfz/Voice.cpp
+++ b/trunk/src/engines/sfz/Voice.cpp
@@ -286,9 +286,15 @@ namespace LinuxSampler { namespace sfz {
 
     void Voice::ProcessGroupEvent(RTList<Event>::Iterator& itEvent) {
         dmsg(4,("Voice %p processGroupEvents event type=%d", (void*)this, itEvent->Type));
+
+        bool ReleaseVoice = (
+            itEvent->Type == Event::type_release_voice &&
+            itEvent->Param.ReleaseVoice.VoiceID == pEngine->GetVoicePool()->getID(this)
+        );
+
         if (itEvent->Type == Event::type_control_change ||
             (Type & Voice::type_controller_triggered) ||
-            itEvent->Param.Note.Key != HostKey()) {
+            ReleaseVoice) {
             dmsg(4,("Voice %p - kill", (void*)this));
             if (pRegion->off_mode == ::sfz::OFF_NORMAL) {
                 // turn off the voice by entering release envelope stage
diff --git a/trunk/src/engines/sfz/Voice.h b/trunk/src/engines/sfz/Voice.h
index 36ca8e6..f31b7a3 100644
--- a/trunk/src/engines/sfz/Voice.h
+++ b/trunk/src/engines/sfz/Voice.h
@@ -54,6 +54,7 @@ namespace LinuxSampler { namespace sfz {
             void SetEngine(LinuxSampler::Engine* pEngine);
             void CalculateFadeOutCoeff(float FadeOutTime, float SampleRate);
             virtual release_trigger_t GetReleaseTriggerFlags() OVERRIDE;
+            virtual int               GetKeyGroup(bool mute = false) OVERRIDE { return mute ? pRegion->off_by : pRegion->group; }
 
             virtual void VoiceFreed() OVERRIDE { SignalRack.Reset(); }
 
@@ -118,6 +119,9 @@ namespace LinuxSampler { namespace sfz {
             friend class EndpointUnit;
             friend class SfzSignalUnitRack;
 
+            // Required to support polyphony
+            friend class EngineChannel;
+
         protected:
             virtual uint8_t CrossfadeAttenuation(uint8_t& CrossfadeControllerValue) OVERRIDE {
                 /*uint8_t c = std::max(CrossfadeControllerValue, pRegion->AttenuationControllerThreshold);
diff --git a/trunk/src/engines/sfz/sfz.cpp b/trunk/src/engines/sfz/sfz.cpp
index 417bc54..2562ccd 100755
--- a/trunk/src/engines/sfz/sfz.cpp
+++ b/trunk/src/engines/sfz/sfz.cpp
@@ -330,8 +330,15 @@ namespace sfz
         trigger = TRIGGER_ATTACK;
 
         group = 0;
-        off_by = 0;
+        // http://www.sfzformat.com/legacy/ states that default off_by is 0,
+        // but that would make all regions "exclusive" by default.
+        off_by = -1;
         off_mode = OFF_FAST;
+        // -1 means no polyphony limit, 0 means no playback at all
+        polyphony = -1;
+        // 0 means no note_polyphony limit
+        note_polyphony = 0;
+        note_selfmask = SELFMASK_ON;
 
         // sample player
         count = optional<int>::nothing;
@@ -639,6 +646,9 @@ namespace sfz
         definition->group = group;
         definition->off_by = off_by;
         definition->off_mode = off_mode;
+        definition->polyphony = polyphony;
+        definition->note_polyphony = note_polyphony;
+        definition->note_selfmask = note_selfmask;
         definition->on_locc = on_locc;
         definition->on_hicc = on_hicc;
 
@@ -1583,6 +1593,13 @@ namespace sfz
             if (value == "fast")  pCurDef->off_mode = OFF_FAST;
             else if (value == "normal") pCurDef->off_mode = OFF_NORMAL;
         }
+        else if ("polyphony" == key) pCurDef->polyphony = ToInt(value);
+        else if ("note_polyphony" == key) pCurDef->note_polyphony = ToInt(value);
+        else if ("note_selfmask" == key)
+        {
+            if (value == "on")  pCurDef->note_selfmask = SELFMASK_ON;
+            else if (value == "off") pCurDef->note_selfmask = SELFMASK_OFF;
+        }
 
         // sample player
         else if ("count" == key) { pCurDef->count = ToInt(value); pCurDef->loop_mode = ONE_SHOT; }
diff --git a/trunk/src/engines/sfz/sfz.h b/trunk/src/engines/sfz/sfz.h
index 381df2e..3c65140 100755
--- a/trunk/src/engines/sfz/sfz.h
+++ b/trunk/src/engines/sfz/sfz.h
@@ -143,6 +143,7 @@ namespace sfz
                        LPF_2P, HPF_2P, BPF_2P, BRF_2P, PKF_2P,
                        LPF_4P, HPF_4P,
                        LPF_6P, HPF_6P };
+    enum note_selfmask_t { SELFMASK_ON, SELFMASK_OFF };
 
     typedef unsigned char trigger_t;
     typedef unsigned char uint8_t;
@@ -427,9 +428,12 @@ namespace sfz
 
         trigger_t trigger;
 
-        uint group;
-        uint off_by;
+        int group;
+        int off_by;
         off_mode_t off_mode;
+        int polyphony;
+        int note_polyphony;
+        note_selfmask_t note_selfmask;
 
         Array<int> on_locc; Array<int> on_hicc;
 
