diff -Naur -x CVS -x osx -x '.*' linuxsampler.orig/configure.in linuxsampler/configure.in
--- linuxsampler.orig/configure.in	2008-02-09 16:51:11.000000000 +0900
+++ linuxsampler/configure.in	2008-02-10 14:08:47.000000000 +0900
@@ -878,6 +878,27 @@
 )
 AC_DEFINE_UNQUOTED(CONFIG_PORTAMENTO_TIME_DEFAULT, $config_portamento_time_default, [Define default portamento time.])
 
+AC_ARG_ENABLE(legato-ctrl,
+  [  --enable-legato-ctrl
+                          MIDI control number for legato
+                          (default=0, no legato control).],
+  [config_legato_ctrl="${enableval}"],
+  [config_legato_ctrl="0"]
+)
+AC_DEFINE_UNQUOTED(CONFIG_LEGATO_CTRL, $config_legato_ctrl, [MIDI control for legato])
+
+AC_ARG_ENABLE(legato-crossfade-time,
+  [  --enable-legato-crossfade-time
+                          Crossfade time for legato play (in seconds)
+                          Non-negative value: fixed value
+                          Negative value: variable value, calculated by (CC-63)*
+                          (legato_crossfade_time)/64.0, where CC is the legato control
+                          value (default=0.15).],
+  [config_legato_crossfade_time="${enableval}"],
+  [config_legato_crossfade_time="0.15"]
+)
+AC_DEFINE_UNQUOTED(CONFIG_LEGATO_CROSSFADE_TIME, $config_legato_crossfade_time, [crossfade time for legato])
+
 AC_ARG_ENABLE(signed-triang-algo,
   [  --enable-signed-triang-algo
                           Signed triangular wave algorithm to be used (e.g. for LFOs).
diff -Naur -x CVS -x osx -x '.*' linuxsampler.orig/src/common/atomic.h linuxsampler/src/common/atomic.h
--- linuxsampler.orig/src/common/atomic.h	2008-01-26 00:06:01.000000000 +0900
+++ linuxsampler/src/common/atomic.h	2008-02-10 09:32:47.000000000 +0900
@@ -1249,6 +1249,8 @@
 /*  TODO: should use atomic routines in CoreServices.framework  */
 #define atomic_inc(a) (++(*a))
 #define atomic_dec(a) (--(*a))
+#define atomic_add(i, a) ((*a) += (i))
+#define atomic_sub(i, a) ((*a) -= (i))
 
 #else
 
diff -Naur -x CVS -x osx -x '.*' linuxsampler.orig/src/engines/gig/Engine.cpp linuxsampler/src/engines/gig/Engine.cpp
--- linuxsampler.orig/src/engines/gig/Engine.cpp	2008-01-24 23:14:21.000000000 +0900
+++ linuxsampler/src/engines/gig/Engine.cpp	2008-02-10 17:54:05.000000000 +0900
@@ -987,18 +987,41 @@
         if (pEngineChannel->SoloMode) {
             Pool<uint>::Iterator itYoungestKey = pEngineChannel->pActiveKeys->last();
             if (itYoungestKey) {
+				RTList<Voice>::Iterator itVoice;
                 const int iYoungestKey = *itYoungestKey;
                 const midi_key_info_t* pOtherKey = &pEngineChannel->pMIDIKeyInfo[iYoungestKey];
                 if (pOtherKey->Active) {
                     // get final portamento position of currently active voice
-                    if (pEngineChannel->PortamentoMode) {
-                        RTList<Voice>::Iterator itVoice = pOtherKey->pActiveVoices->last();
-                        if (itVoice) itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);
+                    if (pEngineChannel->PortamentoMode
+					#if CONFIG_LEGATO_CTRL
+						|| pEngineChannel->LegatoMode
+					#endif
+					) {
+                        itVoice = pOtherKey->pActiveVoices->last();
+                        if (itVoice)
+					#if CONFIG_LEGATO_CTRL
+						{
+							if (pEngineChannel->PortamentoMode) {
+								pEngineChannel->PortamentoTarget = key;
+								itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);
+							}
+							if (pEngineChannel->LegatoMode) {
+								itVoice->TriggerLegatoFadeOut(itNoteOnEventOnKeyList);
+								pEngineChannel->bInLegato = 1;
+							}
+						}
+					#else
+							itVoice->UpdatePortamentoPos(itNoteOnEventOnKeyList);
+					#endif
                     }
                     // kill all voices on the (other) key
                     RTList<Voice>::Iterator itVoiceToBeKilled = pOtherKey->pActiveVoices->first();
                     RTList<Voice>::Iterator end               = pOtherKey->pActiveVoices->end();
                     for (; itVoiceToBeKilled != end; ++itVoiceToBeKilled) {
+						#if CONFIG_LEGATO_CTRL
+						if (pEngineChannel->LegatoMode && itVoice == itVoiceToBeKilled)
+								continue;
+						#endif
                         if (itVoiceToBeKilled->Type != Voice::type_release_trigger)
                             itVoiceToBeKilled->Kill(itNoteOnEventOnKeyList);
                     }
@@ -1040,6 +1063,9 @@
                 for (int i = 0; i < voicesRequired; i++)
                     LaunchVoice(pEngineChannel, itNoteOnEventOnKeyList, i, false, true, true);
             }
+			#if CONFIG_LEGATO_CTRL
+			pEngineChannel->bInLegato = 0;
+			#endif
         }
 
         // if neither a voice was spawned or postponed then remove note on event from key again
@@ -1747,6 +1773,18 @@
                 }
                 break;
             }
+			#if CONFIG_LEGATO_CTRL
+			case CONFIG_LEGATO_CTRL: { // legato on / off
+				int ival = itControlChangeEvent->Param.CC.Value;
+				float fval = CONFIG_LEGATO_CROSSFADE_TIME;
+				pEngineChannel->LegatoMode = (ival >= 64);
+				if (fval >= 0.0)
+					pEngineChannel->LegatoTime = fval;
+				else
+					pEngineChannel->LegatoTime = (-fval) * (ival - 63) / 64.0;
+				break;
+			}
+			#endif
             case 100: { // RPN controller LSB
                 pEngineChannel->SetMidiRpnControllerLsb(itControlChangeEvent->Param.CC.Value);
                 break;
diff -Naur -x CVS -x osx -x '.*' linuxsampler.orig/src/engines/gig/EngineChannel.h linuxsampler/src/engines/gig/EngineChannel.h
--- linuxsampler.orig/src/engines/gig/EngineChannel.h	2008-02-09 16:51:12.000000000 +0900
+++ linuxsampler/src/engines/gig/EngineChannel.h	2008-02-10 09:46:15.000000000 +0900
@@ -117,6 +117,14 @@
             bool                    PortamentoMode;           ///< in Portamento Mode we slide the pitch from the last note to the current note.
             float                   PortamentoTime;           ///< How long it will take to glide from the previous note to the current (in seconds)
             float                   PortamentoPos;            ///< Current position on the keyboard, that is integer and fractional part (only used if PortamentoMode is on)
+
+			#if CONFIG_LEGATO_CTRL
+			float                   PortamentoTarget;         ///< Portamento destination position (only used during legato crossfade)
+			bool                    LegatoMode;               ///< in Legato Mode we crossfade the volume from the last note to the current note.
+			float                   LegatoTime;               ///< How long it will take to crossfade between two notes (in seconds)
+			bool                    bInLegato;
+			#endif
+
             double                  GlobalVolume;             ///< Master volume factor set through the C++ API / LSCP (a value < 1.0 means attenuation, a value > 1.0 means amplification)
             double                  MidiVolume;               ///< Volume factor altered by MIDI CC#7 (a value < 1.0 means attenuation, a value > 1.0 means amplification)
             float                   GlobalPanLeft;
diff -Naur -x CVS -x osx -x '.*' linuxsampler.orig/src/engines/gig/Voice.cpp linuxsampler/src/engines/gig/Voice.cpp
--- linuxsampler.orig/src/engines/gig/Voice.cpp	2007-09-04 10:12:48.000000000 +0900
+++ linuxsampler/src/engines/gig/Voice.cpp	2008-02-13 19:40:30.000000000 +0900
@@ -302,6 +302,19 @@
             dmsg(5,("PortamentoPos=%f, depth=%f, time=%f\n", pEngineChannel->PortamentoPos, eg3depth, eg3time));
         }
 
+		#if CONFIG_LEGATO_CTRL
+		// setup Legato EG
+		if (pEngineChannel->LegatoMode && pEngineChannel->bInLegato) {
+			LegatoEG.trigger(0.0, pEngineChannel->LegatoTime, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
+			LegatoState = legato_state_fadein;
+			#ifdef CONFIG_INTERPOLATE_VOLUME
+            finalSynthesisParameters.fFinalVolumeLeft  = 0;
+            finalSynthesisParameters.fFinalVolumeRight = 0;
+			#endif
+		} else {
+			LegatoState = legato_state_disable;
+		}
+		#endif
 
         // setup LFO 1 (VCA LFO)
         {
@@ -836,7 +849,24 @@
                     fFinalCutoff *= EG2.getLevel();
                     break; // noop
             }
-            if (EG3.active()) finalSynthesisParameters.fFinalPitch *= EG3.render();
+			if (EG3.active()) {
+				finalSynthesisParameters.fFinalPitch *= EG3.render();
+			}
+
+			#if CONFIG_LEGATO_CTRL
+			if (LegatoState != legato_state_disable && LegatoState != legato_state_sustain) {
+				float fval = LegatoEG.render();
+				if (LegatoState == legato_state_fadeout)
+					fval = 1.0 - fval;
+				if (fval > 0.75)
+					fval = 1.0;
+				else
+					fval = fval / 0.75;
+				fFinalVolume *= fval;
+				if (LegatoState == legato_state_fadeout)
+					finalSynthesisParameters.fFinalPitch *= LegatoPortamentoRatio;
+			}
+            #endif           
 
             // process low frequency oscillators
             if (bLFO1Enabled) fFinalVolume *= (1.0f - pLFO1->render());
@@ -871,6 +901,7 @@
             finalSynthesisParameters.fFinalVolumeRight =
                 fFinalVolume * VolumeRight * PanRightSmoother.render();
 #endif
+			
             // render audio for one subfragment
             RunSynthesisFunction(SynthesisMode, &finalSynthesisParameters, &loop);
 
@@ -897,6 +928,22 @@
             EG3.increment(1);
             if (!EG3.toEndLeft()) EG3.update(); // neutralize envelope coefficient if end reached
 
+			#if CONFIG_LEGATO_CTRL
+			if (LegatoState != legato_state_disable && LegatoState != legato_state_sustain) {
+				LegatoEG.increment(1);
+				if (!LegatoEG.toEndLeft()) {
+					LegatoEG.update();
+					if (LegatoState == legato_state_fadein)
+						LegatoState = legato_state_sustain;
+					else {
+						//  End of fade-out -> enter release stage of EG1
+					//	EG1.update(EGADSR::event_release, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
+						EG1.enterFadeOutStage();
+					}
+				}
+			}
+			#endif
+
             Pos = newPos;
             i = iSubFragmentEnd;
         }
@@ -915,6 +962,34 @@
         pEngineChannel->PortamentoPos = (float) MIDIKey + RTMath::FreqRatioToCents(fFinalEG3Level) * 0.01f;
     }
 
+	#if CONFIG_LEGATO_CTRL
+     /** @brief Trigger fade-out stage for legato mode.
+      *
+      * Will be called when legato mode is enabled and a new note is pressed on.
+	  * Triggers LegatoEG in fade-out mode and also EG3 if portamento mode is on.
+      */
+	void Voice::TriggerLegatoFadeOut(Pool<Event>::Iterator& itNoteOffEvent) {
+		if (pEngineChannel->LegatoMode) {
+			float startLevel = 0.0;
+			int32_t pos = itNoteOffEvent->FragmentPos();
+			if (LegatoEG.active()) {
+				if (LegatoState == legato_state_fadein)
+					startLevel = 1.0 - LegatoEG.level(pos);
+				else if (LegatoState == legato_state_fadeout)
+					startLevel = LegatoEG.level(pos);
+			}
+			LegatoEG.trigger(startLevel, pEngineChannel->LegatoTime, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
+			LegatoState = legato_state_fadeout;
+		}
+		if (pEngineChannel->PortamentoMode) {
+			float eg3depth = RTMath::CentsToFreqRatio((pEngineChannel->PortamentoPos - pEngineChannel->PortamentoTarget) * 100);
+			float eg3time = pEngineChannel->PortamentoTime;
+			EG3.trigger(eg3depth, eg3time, pEngine->SampleRate / CONFIG_DEFAULT_SUBFRAGMENT_SIZE);
+			LegatoPortamentoRatio = 1.0 / eg3depth;
+		} else LegatoPortamentoRatio = 1.0;
+	}
+	#endif
+
     /**
      *  Immediately kill the voice. This method should not be used to kill
      *  a normal, active voice, because it doesn't take care of things like
diff -Naur -x CVS -x osx -x '.*' linuxsampler.orig/src/engines/gig/Voice.h linuxsampler/src/engines/gig/Voice.h
--- linuxsampler.orig/src/engines/gig/Voice.h	2007-10-15 07:00:16.000000000 +0900
+++ linuxsampler/src/engines/gig/Voice.h	2008-02-12 01:14:17.000000000 +0900
@@ -122,6 +122,9 @@
             inline bool IsActive() { return PlaybackState; }
             inline bool IsStealable() { return !itKillEvent && PlaybackState >= playback_state_ram; }
             void UpdatePortamentoPos(Pool<Event>::Iterator& itNoteOffEvent);
+			#if CONFIG_LEGATO_CTRL
+			void TriggerLegatoFadeOut(Pool<Event>::Iterator& itNoteOffEvent);
+			#endif
 
         //private:
             // Types
@@ -159,6 +162,17 @@
             EGADSR                      EG1;                ///< Envelope Generator 1 (Amplification)
             EGADSR                      EG2;                ///< Envelope Generator 2 (Filter cutoff frequency)
             EGDecay                     EG3;                ///< Envelope Generator 3 (Pitch)
+			#if CONFIG_LEGATO_CTRL
+			enum legato_state_t {
+				legato_state_disable = 0,
+				legato_state_fadein = 1,
+				legato_state_sustain = 2,
+				legato_state_fadeout = 3
+			};
+			EGDecay                     LegatoEG;           ///< Envelope Generator for legato mode
+			legato_state_t              LegatoState;
+			float                       LegatoPortamentoRatio; ///< During the fade-out stage, the frequency is multiplied by EG3.render() times this value
+			#endif
             midi_ctrl                   VCFCutoffCtrl;
             midi_ctrl                   VCFResonanceCtrl;
             LFOUnsigned*                pLFO1;               ///< Low Frequency Oscillator 1 (Amplification)
