diff --git a/libraries/MeasCtrl/MeasCtrl.cpp b/libraries/MeasCtrl/MeasCtrl.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4e43acb1b2670fc4b9fe25d9d0d69fa04d14a4c4
--- /dev/null
+++ b/libraries/MeasCtrl/MeasCtrl.cpp
@@ -0,0 +1,106 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   MeasMuse.cpp
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+// Datum:   14. November 2022
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (siehe Wikipedia: Creative Commons)
+//
+
+#include "MeasCtrl.h"
+
+// ----------------------------------------------------------------------------
+// Konstruktoren und Initialisierungen
+// ----------------------------------------------------------------------------
+//
+MeasCtrl::MeasCtrl()
+{
+}
+
+// ----------------------------------------------------------------------------
+// Hilfsfunktionen
+// ----------------------------------------------------------------------------
+//
+
+// ----------------------------------------------------------------------------
+// Konfiguration
+// ----------------------------------------------------------------------------
+//
+void MeasCtrl::setBand(int idx, int band, float min, float max)
+{
+  bandList[idx][band].minVal = min;
+  bandList[idx][band].maxVal = max;
+}
+
+void MeasCtrl::setDelta(int idx, float dVal)
+{
+  bandDeltaVal[idx] = dVal;
+}
+
+
+// ----------------------------------------------------------------------------
+// Anwenderfunktionen
+// ----------------------------------------------------------------------------
+//
+
+// Messwert klassieren und Häufigkeit zurückgeben
+//
+int MeasCtrl::setBandCount(int idx, float val)
+{
+  BandPtr bPtr;
+  int     retv;
+  float   deltaVal;
+
+  sumCounter[idx]++;
+
+  for(int i = 0; i < DepthBandArrays; i++)
+  {
+    bPtr = &bandList[idx][i];
+    if(val >= bPtr->minVal && val < bPtr->maxVal)
+    {
+      bPtr->countVal++;
+      deltaVal = val - bandOldVal[idx];
+      if(deltaVal > 0 && deltaVal >= bandDeltaVal[idx])
+        bPtr->countDeltaUp++;
+      else if(deltaVal < 0 && deltaVal <= -bandDeltaVal[idx])
+        bPtr->countDeltaDown++;
+      bandOldVal[idx] = val;
+      return(bPtr->countVal);
+    }
+  }
+  return(-1);
+}
+
+int MeasCtrl::getDeltaCount(int idx, int band, bool reset)
+{
+  int retv;
+
+  retv = bandList[idx][band].countVal - bandList[idx][band].countLast;
+  if(reset)
+    bandList[idx][band].countLast = bandList[idx][band].countVal;
+  return(retv);
+}
+
+int MeasCtrl::getDeltaCount(int idx, int band)
+{
+  return(getDeltaCount(idx, band, false));
+}
+
+bool  MeasCtrl::testDeltaCount(int idx, int band, int val)
+{
+  int delta = bandList[idx][band].countVal - bandList[idx][band].countLast;
+  if(delta < val) return(false);
+  bandList[idx][band].countLast = bandList[idx][band].countVal;
+  return(true);
+}
+
+void  MeasCtrl::resetDeltaCount(int idx, int band)
+{
+  bandList[idx][band].countLast = bandList[idx][band].countVal;
+}
+
+
+
+
+
diff --git a/libraries/MeasCtrl/MeasCtrl.h b/libraries/MeasCtrl/MeasCtrl.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5584a91d96c3317bfb223219238ecb6367b705f
--- /dev/null
+++ b/libraries/MeasCtrl/MeasCtrl.h
@@ -0,0 +1,108 @@
+//-----------------------------------------------------------------------------
+// Thema:   Social Manufacturing Network / Development Environment
+// Datei:   MeasCtrl.h
+// Editor:  Robert Patzke
+// URI/URL: www.mfp-portal.de
+// Datum:   15. Oktober 2023
+//-----------------------------------------------------------------------------
+// Lizenz:  CC-BY-SA  (siehe Wikipedia: Creative Commons)
+//
+#ifndef _MeasCtrl_h
+#define _MeasCtrl_h
+//-----------------------------------------------------------------------------
+
+#include "arduinoDefs.h"
+
+//#define MeasCtrlDebug
+
+#define NrBandArrays    16
+#define DepthBandArrays 9
+
+// ----------------------------------------------------------------------------
+// Allgemeine Datentypen
+// ----------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+// class MeasCtrl
+// ---------------------------------------------------------------------------
+// Abbildung von Messwerten auf Steuerfunktionen, Klassenbildung
+// Umgebung inklusive der Konfiguration
+
+class MeasCtrl
+{
+public:
+  // -------------------------------------------------------------------------
+  // globale klassenspezifische Datentypen
+  // -------------------------------------------------------------------------
+  //
+  typedef struct _Band
+  {
+    float minVal;
+    float maxVal;
+    dword countVal;
+    dword countDeltaUp;
+    dword countDeltaDown;
+    dword countLast;
+  }Band, *BandPtr;
+
+
+private:
+  // -------------------------------------------------------------------------
+  // lokale Datentypen
+  // -------------------------------------------------------------------------
+  //
+
+  // -------------------------------------------------------------------------
+  // Hilfsfunktionen
+  // -------------------------------------------------------------------------
+  //
+
+public:
+  // -------------------------------------------------------------------------
+  // Konstruktoren und Initialisierungen
+  // -------------------------------------------------------------------------
+  //
+  MeasCtrl();
+
+
+private:
+  // -------------------------------------------------------------------------
+  // lokale Variablen
+  // -------------------------------------------------------------------------
+  //
+  Band  bandList[NrBandArrays][DepthBandArrays];  // Werteklassen
+  float bandOldVal[NrBandArrays];                 // Letzter Eingang
+  float bandDeltaVal[NrBandArrays];               // Änderungskennung
+  dword sumCounter[NrBandArrays];                 // Gesamtmesswertzähler
+
+public:
+  // -------------------------------------------------------------------------
+  // Konfiguration
+  // -------------------------------------------------------------------------
+  //
+  void setBand(int idx, int band, float min, float max);
+  void setDelta(int idx, float dVal);
+
+  // -------------------------------------------------------------------------
+  // Anwenderfunktionen
+  // -------------------------------------------------------------------------
+  //
+  int   setBandCount(int idx, float val);
+  int   getDeltaCount(int idx, int band);
+  int   getDeltaCount(int idx, int band, bool reset);
+  bool  testDeltaCount(int idx, int band, int val);
+  void  resetDeltaCount(int idx, int band);
+
+  // -------------------------------------------------------------------------
+  // Debugfunktionen
+  // -------------------------------------------------------------------------
+  //
+#ifdef MeasCtrlDebug
+
+#endif
+
+
+};
+
+//-----------------------------------------------------------------------------
+#endif // _MeasMuse_h
diff --git a/libraries/MeasMuse/MeasMuse.cpp b/libraries/MeasMuse/MeasMuse.cpp
index ecf87ce3093db338f0b3b172ea706f46bc8248f5..32eddd58ae1bae6b3a54a46b71da534d9909cf37 100644
--- a/libraries/MeasMuse/MeasMuse.cpp
+++ b/libraries/MeasMuse/MeasMuse.cpp
@@ -24,12 +24,15 @@ MeasMuse::MeasMuse()
 //
 void MeasMuse::Posture2Midi::setKoeffRoll(MeasMap map)
 {
+  mapRoll = map;
+
   switch(map)
   {
     case BiLinear:
+    case Linear2Scale:
       koeffRoll = (midiArea[aimRoll].high - midiArea[aimRoll].low)
                 / (borderHighRoll - borderLowRoll);
-      midiArea[aimRoll].offset = midiArea[aimRoll].low - (byte) koeffRoll * borderLowRoll;
+      midiArea[aimRoll].offset = midiArea[aimRoll].low - (byte) (koeffRoll * borderLowRoll);
       break;
 
     default:
@@ -39,12 +42,15 @@ void MeasMuse::Posture2Midi::setKoeffRoll(MeasMap map)
 
 void MeasMuse::Posture2Midi::setKoeffPitch(MeasMap map)
 {
+  mapPitch = map;
+
   switch(map)
   {
     case BiLinear:
+    case Linear2Scale:
       koeffPitch = (midiArea[aimPitch].high - midiArea[aimPitch].low)
                 / (borderHighPitch - borderLowPitch);
-      midiArea[aimPitch].offset = midiArea[aimPitch].low - (byte) koeffPitch * borderLowPitch;
+      midiArea[aimPitch].offset = midiArea[aimPitch].low - (byte) (koeffPitch * borderLowPitch);
       break;
 
     default:
@@ -54,12 +60,15 @@ void MeasMuse::Posture2Midi::setKoeffPitch(MeasMap map)
 
 void MeasMuse::Posture2Midi::setKoeffYaw(MeasMap map)
 {
+  mapYaw = map;
+
   switch(map)
   {
     case BiLinear:
+    case Linear2Scale:
       koeffYaw = (midiArea[aimYaw].high - midiArea[aimYaw].low)
                 / (borderHighYaw - borderLowYaw);
-      midiArea[aimYaw].offset = midiArea[aimYaw].low - (byte) koeffYaw * borderLowYaw;
+      midiArea[aimYaw].offset = midiArea[aimYaw].low - (byte) (koeffYaw * borderLowYaw);
       break;
 
     default:
@@ -67,33 +76,94 @@ void MeasMuse::Posture2Midi::setKoeffYaw(MeasMap map)
   }
 }
 
+// ---------------------------------------------------------------------------------
+// Abbildung des Roll-Messwertes auf einen Midi-Wert (0-127)
+// ---------------------------------------------------------------------------------
+//
 int MeasMuse::Posture2Midi::getResultRoll(MidiResultPtr refResult, float measValue)
 {
+  float refVal;
+
   float inVal = measValue + offsetRoll;
+  // Der Messwert wird mit einem einstellbaren Offset verschoben
+
 #ifdef MeasMuseDebug
   debInValRoll = inVal;
 #endif
+
   if(inVal < borderLowRoll) return(-1);
   if(inVal > borderHighRoll) return(1);
-  byte value = midiArea[aimRoll].offset + (byte) (koeffRoll * inVal);
-  if(refResult->value != value)
+  // Wenn der verschobene Messwert außerhalb einstellbarer Grenzen liegt,
+  // dann wird die Abbildung abgebrochen (Rückgabe -1 = Fehler)
+
+  byte dValue = (byte) (koeffRoll * inVal);
+  byte value = midiArea[aimRoll].offset + dValue;
+  // Der Messwert wird mit einem (parametrierten) Koeffizienten auf
+  // einen Midi-Wert (byte) abgebildet und danach mit einem ebenfalls
+  // parametrierten Wert im Midi-Bereich verschoben.
+
+  if(doGapRoll)     // Es kann ein Leerbereich für den Messwert definiert werden
   {
-    refResult->value  = value;
-    refResult->newVal = true;
+    refVal = (float) dValue / koeffRoll;
+    // Zunächst wird ein Referenzmesswert direkt aus dem Midi-Wert berechnet.
+
+    if((inVal < refVal - gapRoll) || (inVal > refVal + gapRoll))
+      return(0);
+    // Wenn der eingegangen Messwert zu weit weg vom Referenzmesswert liegt,
+    // dann wird der Messwert nicht als neuer Messwert gekennzeichnet.
+    // Aber Rückgabe 0, da keine grundsätzliche Bereichsverletzung
   }
-  refResult->type   = aimRoll;
+
+  if(mapRoll == Linear2Scale)   // Extra Listenzuordnung bei Midi-Ergebnis (Tonart)
+  {
+    value = rollKeyScale[value];
+  }
+
+  if(refResult->value != value)     // Wenn ein neuer Midi-Wert entstanden ist
+  {
+    refResult->value  = value;      // Dann wird er im Ergebnis eingetragen
+    refResult->newVal = true;       // und für den Anwender als neu gekennzeichnet
+  }
+
+  refResult->type   = aimRoll;      // Der Wert wird für die Verarbeitung gekennzeichnet
+
 #ifdef MeasMuseDebug
   debResVal = value;
 #endif
+
   return(0);
 }
 
 int MeasMuse::Posture2Midi::getResultPitch(MidiResultPtr refResult, float measValue)
 {
+  float refVal;
   float inVal = measValue + offsetPitch;
   if(inVal < borderLowPitch) return(-1);
   if(inVal > borderHighPitch) return(1);
-  byte value = midiArea[aimPitch].offset + (byte) (koeffPitch * inVal);
+  byte dValue = (byte) (koeffPitch * inVal);
+  byte value = midiArea[aimPitch].offset + dValue;
+
+  if(doGapPitch)
+  {
+    refVal = (float) dValue / koeffPitch;
+
+    if((inVal < refVal - gapPitch) || (inVal > refVal + gapPitch))
+      return(0);
+  }
+
+#ifdef MeasMuseDebug
+  debPitchVal = value;
+#endif
+
+  if(mapPitch == Linear2Scale)   // Extra Listenzuordnung bei Midi-Ergebnis (Tonart)
+  {
+    value = pitchKeyScale[value];
+  }
+
+#ifdef MeasMuseDebug
+  debPitchScaleVal = value;
+#endif
+
   if(refResult->value != value)
   {
     refResult->value  = value;
@@ -108,7 +178,14 @@ int MeasMuse::Posture2Midi::getResultYaw(MidiResultPtr refResult, float measValu
   float inVal = measValue + offsetYaw;
   if(inVal < borderLowYaw) return(-1);
   if(inVal > borderHighYaw) return(1);
+
   byte value = midiArea[aimYaw].offset + (byte) (koeffYaw * inVal);
+
+  if(mapYaw == Linear2Scale)   // Extra Listenzuordnung bei Midi-Ergebnis (Tonart)
+  {
+    value = yawKeyScale[value];
+  }
+
   if(refResult->value != value)
   {
     refResult->value  = value;
@@ -119,9 +196,56 @@ int MeasMuse::Posture2Midi::getResultYaw(MidiResultPtr refResult, float measValu
 }
 
 
+// ----------------------------------------------------------------------------
+// Hilfsfunktionen
+// ----------------------------------------------------------------------------
+//
+byte MeasMuse::createMajorScale(byte *scaleRef, byte startVal)
+{
+  int i = 0;
+  scaleRef[i++] = startVal;     // z.B. C / Do
+  startVal += 2;                // Ganzton höher
+  scaleRef[i++] = startVal;     // z.B. D / Re
+  startVal += 2;                // Ganzton höher
+  scaleRef[i++] = startVal;     // z.B. E / Mi
+  startVal++;                   // Halbton höher
+  scaleRef[i++] = startVal;     // z.B. F / Fa
+  startVal += 2;                // Ganzton höher
+  scaleRef[i++] = startVal;     // z.B. G / So
+  startVal += 2;                // Ganzton höher
+  scaleRef[i++] = startVal;     // z.B. A / La
+  startVal += 2;                // Ganzton höher
+  scaleRef[i++] = startVal;     // z.B. H / Ti
+  startVal++;                   // Halbton höher
+  return(startVal);             // z.B. C' (Do, Start nächste Oktave)
+}
+
+byte MeasMuse::createMinorScale(byte *scaleRef, byte startVal)
+{
+  int i = 0;
+  scaleRef[i++] = startVal;     // z.B. C
+  startVal += 2;                // Ganzton höher
+  scaleRef[i++] = startVal;     // z.B. D
+  startVal++;                   // Halbton höher
+  scaleRef[i++] = startVal;     // z.B. Dis / Es
+  startVal += 2;                // Ganzton höher
+  scaleRef[i++] = startVal;     // z.B. F
+  startVal += 2;                // Ganzton höher
+  scaleRef[i++] = startVal;     // z.B. G
+  startVal++;                   // Halbton höher
+  scaleRef[i++] = startVal;     // z.B. Gis / As
+  startVal += 2;                // Ganzton höher
+  scaleRef[i++] = startVal;     // z.B. B
+  startVal++;                   // Halbton höher
+  return(startVal);             // z.B. C' (Start nächste Oktave)
+}
+
 // ----------------------------------------------------------------------------
 // Konfiguration
 // ----------------------------------------------------------------------------
+// Zunächst sind die folgenden Funktionen für die Lagewinkel extra
+// ausgeführt (kopiert und angepasst), weil noch nicht klar
+// ist, ob die Abbildungen der Winkel ähnlich bleiben werden.
 //
 void MeasMuse::setRollArea(int channel, float offset, float min, float max)
 {
@@ -130,6 +254,12 @@ void MeasMuse::setRollArea(int channel, float offset, float min, float max)
   config[channel].borderHighRoll  = max;
 }
 
+void MeasMuse::setRollGap(int channel, float gap)
+{
+  config[channel].gapRoll = gap;
+  config[channel].doGapRoll = true;
+}
+
 void MeasMuse::setPitchArea(int channel, float offset, float min, float max)
 {
   config[channel].offsetPitch      = offset;
@@ -137,6 +267,12 @@ void MeasMuse::setPitchArea(int channel, float offset, float min, float max)
   config[channel].borderHighPitch  = max;
 }
 
+void MeasMuse::setPitchGap(int channel, float gap)
+{
+  config[channel].gapPitch = gap;
+  config[channel].doGapPitch = true;
+}
+
 void MeasMuse::setYawArea(int channel, float offset, float min, float max)
 {
   config[channel].offsetYaw      = offset;
@@ -144,6 +280,12 @@ void MeasMuse::setYawArea(int channel, float offset, float min, float max)
   config[channel].borderHighYaw  = max;
 }
 
+void MeasMuse::setYawGap(int channel, float gap)
+{
+  config[channel].gapYaw = gap;
+  config[channel].doGapYaw = true;
+}
+
 void MeasMuse::setAims(int channel, Meas2Midi aimRoll, Meas2Midi aimPitch, Meas2Midi aimYaw)
 {
   config[channel].aimRoll   = aimRoll;
@@ -164,6 +306,101 @@ void MeasMuse::setMapping(int channel, MeasMap mapRoll, MeasMap mapPitch, MeasMa
   config[channel].setKoeffYaw(mapYaw);
 }
 
+void MeasMuse::setOpMode(int channel, int opMode)
+{
+  config[channel].midiOpMode = opMode;
+  config[channel].ready = true;
+}
+
+// --------------------------------------------------------
+// Zuordnung von Listen (Array, Tabelle) zu den Midi-Werten
+// --------------------------------------------------------
+//
+void MeasMuse::setRollKeyScale(int channel, int minNoteVal, int nrOfNotes, ScaleType type)
+{
+  div_t nrOcts;
+  byte  octStartVal;
+
+  if(nrOfNotes > 128) nrOfNotes = 128;    // Auf 128 Midiwerte eingeschränkt
+  if(nrOfNotes < 2) nrOfNotes = 2;        // Mindestens 2 Werte
+
+  if(minNoteVal < 0 || (minNoteVal + nrOfNotes) > 127) minNoteVal = 0;
+  // Der Anfangswert (Grundton bei Tonleiter) wird eingeschränkt.
+
+  nrOcts = div(nrOfNotes,12);   // Anzahl der Oktaven
+  if(nrOcts.rem > 0)            // Wenn mehr Noten sind,
+    nrOcts.quot++;              // dann noch eine Oktave dranhängen
+
+  octStartVal = minNoteVal;     // Startwert für die Liste
+
+  // Aufbau der wählbaren Tonleitern, zunächst nur klassisch DUR und MOLL
+  //
+  for(int i = 0; i < nrOcts.quot; i++)
+  {
+  if(type == stMajor)
+    octStartVal = createMajorScale(&config[channel].rollKeyScale[7*i], octStartVal);
+  else
+    octStartVal = createMinorScale(&config[channel].rollKeyScale[7*i], octStartVal);
+  }
+}
+
+
+void MeasMuse::setPitchKeyScale(int channel, int minNoteVal, int nrOfNotes, ScaleType type)
+{
+  div_t nrOcts;
+  byte  octStartVal;
+
+  if(nrOfNotes > 128) nrOfNotes = 128;    // Auf 128 Midiwerte eingeschränkt
+  if(nrOfNotes < 2) nrOfNotes = 2;        // Mindestens 2 Werte
+
+  if(minNoteVal < 0 || (minNoteVal + nrOfNotes) > 127) minNoteVal = 0;
+  // Der Anfangswert (Grundton bei Tonleiter) wird eingeschränkt.
+
+  nrOcts = div(nrOfNotes,12);   // Anzahl der Oktaven
+  if(nrOcts.rem > 0)            // Wenn mehr Noten sind,
+    nrOcts.quot++;              // dann noch eine Oktave dranhängen
+
+  octStartVal = minNoteVal;     // Startwert für die Liste
+
+  // Aufbau der wählbaren Tonleitern, zunächst nur klassisch DUR und MOLL
+  //
+  for(int i = 0; i < nrOcts.quot; i++)
+  {
+  if(type == stMajor)
+    octStartVal = createMajorScale(&config[channel].pitchKeyScale[7*i], octStartVal);
+  else
+    octStartVal = createMinorScale(&config[channel].pitchKeyScale[7*i], octStartVal);
+  }
+}
+
+void MeasMuse::setYawKeyScale(int channel, int minNoteVal, int nrOfNotes, ScaleType type)
+{
+  div_t nrOcts;
+  byte  octStartVal;
+
+  if(nrOfNotes > 128) nrOfNotes = 128;    // Auf 128 Midiwerte eingeschränkt
+  if(nrOfNotes < 2) nrOfNotes = 2;        // Mindestens 2 Werte
+
+  if(minNoteVal < 0 || (minNoteVal + nrOfNotes) > 127) minNoteVal = 0;
+  // Der Anfangswert (Grundton bei Tonleiter) wird eingeschränkt.
+
+  nrOcts = div(nrOfNotes,12);   // Anzahl der Oktaven
+  if(nrOcts.rem > 0)            // Wenn mehr Noten sind,
+    nrOcts.quot++;              // dann noch eine Oktave dranhängen
+
+  octStartVal = minNoteVal;     // Startwert für die Liste
+
+  // Aufbau der wählbaren Tonleitern, zunächst nur klassisch DUR und MOLL
+  //
+  for(int i = 0; i < nrOcts.quot; i++)
+  {
+  if(type == stMajor)
+    octStartVal = createMajorScale(&config[channel].yawKeyScale[7*i], octStartVal);
+  else
+    octStartVal = createMinorScale(&config[channel].yawKeyScale[7*i], octStartVal);
+  }
+}
+
 
 // ----------------------------------------------------------------------------
 // Anwenderfunktionen
@@ -184,3 +421,9 @@ int MeasMuse::resultYaw(int channel, MidiResultPtr refResult, float measValue)
   return(config[channel].getResultYaw(refResult, measValue));
 }
 
+int MeasMuse::getOpMode(int channel)
+{
+  if(!config[channel].ready) return(-1);
+  else return(config[channel].midiOpMode);
+}
+
diff --git a/libraries/MeasMuse/MeasMuse.h b/libraries/MeasMuse/MeasMuse.h
index d2501aa14db60b6d8b5de61e429b55113564c8ad..bb43103719d92ca37d14378f508d6b759357f48f 100644
--- a/libraries/MeasMuse/MeasMuse.h
+++ b/libraries/MeasMuse/MeasMuse.h
@@ -12,10 +12,12 @@
 //-----------------------------------------------------------------------------
 
 #include "arduinoDefs.h"
+#include "MidiNotes.h"
 
 //#define MeasMuseDebug
 
-#define NrOfChannelsMM  16
+#define NrOfChannelsMM    16
+#define MaxNrOfScaleNotes 256
 
 // ----------------------------------------------------------------------------
 // Datentypen
@@ -48,6 +50,7 @@ enum MeasMap
 {
   OneToOne,     // Direkte Wertübertragung
   BiLinear,     // Linear auf Linear
+  Linear2Scale, // Linear auf Tonleiter
   NrMM
 };
 
@@ -65,6 +68,27 @@ typedef struct _MidiArea
   byte  offset;
 } MidiArea, *MidiAreaPtr;
 
+typedef enum _keyNoteVal
+{
+  Tonic_C = 12,
+  Tonic_Cis,
+  Tonic_D,
+  Tonic_Dis,
+  Tonic_E,
+  Tonic_F,
+  Tonic_Fis,
+  Tonic_G,
+  Tonic_Gis,
+  Tonic_A,
+  Tonic_B,
+  Tonic_H
+} KeyNoteVal;
+
+typedef enum _scaleType
+{
+  stMajor,
+  stMinor
+} ScaleType;
 
 // ---------------------------------------------------------------------------
 // class MeasMuse
@@ -85,24 +109,48 @@ public:
   public:
     // Konfigurationsdaten
     // -----------------------------------------------------------------------
+    // Zunächst sind die folgenden Variablen für die Lagewinkel extra
+    // ausgeführt (kopiert und angepasst), weil noch nicht klar
+    // ist, ob die Datenstrukturen für die Winkel gleich bleiben wird.
+
     float       offsetRoll;
     float       borderLowRoll;
     float       borderHighRoll;
     float       koeffRoll;
+    float       gapRoll;
+    bool        doGapRoll;
+    MeasMap     mapRoll;
+    Meas2Midi   aimRoll;
+    byte        rollKeyScale[MaxNrOfScaleNotes];      //Tonleiter
+
     float       offsetPitch;
     float       borderLowPitch;
     float       borderHighPitch;
     float       koeffPitch;
+    float       gapPitch;
+    bool        doGapPitch;
+    MeasMap     mapPitch;
+    Meas2Midi   aimPitch;
+    byte        pitchKeyScale[MaxNrOfScaleNotes];     //Tonleiter
+
     float       offsetYaw;
     float       borderLowYaw;
     float       borderHighYaw;
     float       koeffYaw;
-    Meas2Midi   aimRoll;
-    Meas2Midi   aimPitch;
+    float       gapYaw;
+    bool        doGapYaw;
+    MeasMap     mapYaw;
     Meas2Midi   aimYaw;
+    byte        yawKeyScale[MaxNrOfScaleNotes];       //Tonleiter
+
     byte        signAreaValue[4];
     byte        signAreaCtrl[4];
+    int         midiOpMode;
     MidiArea    midiArea[NrM2M];
+    bool        ready;
+
+    byte        keyStart;
+    int         keyNrNotes;
 
     // Hilfsfunktionen
     // -----------------------------------------------------------------------
@@ -115,6 +163,7 @@ public:
     int  getResultPitch(MidiResultPtr refResult, float measValue);
     int  getResultYaw(MidiResultPtr refResult, float measValue);
 
+
 #ifdef MeasMuseDebug
 
     // Debugging
@@ -123,6 +172,9 @@ public:
     float   debInValRoll;
     byte    debResVal;
 
+    byte    debPitchVal;
+    byte    debPitchScaleVal;
+
 #endif
   };
 
@@ -143,11 +195,23 @@ private:
   // Hilfsfunktionen
   // -------------------------------------------------------------------------
   //
+  byte createMajorScale(byte *scaleRef, byte startVal);
+  byte createMinorScale(byte *scaleRef, byte startVal);
 
-private:
+
+  // -------------------------------------------------------------------------
+  // Debugfunktionen
+  // -------------------------------------------------------------------------
+  //
 #ifdef MeasMuseDebug
+
+  char  debMsg[256];
+
 public:
+  char *getPtchValues();
 #endif
+
+private:
   // -------------------------------------------------------------------------
   // lokale Variablen
   // -------------------------------------------------------------------------
@@ -166,11 +230,21 @@ public:
   // -------------------------------------------------------------------------
   //
   void setRollArea(int channel, float offset, float min, float max);
+  void setRollGap(int channel, float gap);
   void setPitchArea(int channel, float offset, float min, float max);
+  void setPitchGap(int channel, float gap);
   void setYawArea(int channel, float offset, float min, float max);
+  void setYawGap(int channel, float gap);
   void setAims(int channel, Meas2Midi aimRoll, Meas2Midi aimPitch, Meas2Midi aimYaw);
   void setMidiArea(int channel, Meas2Midi midi, byte low, byte high);
   void setMapping(int channel, MeasMap mapRoll, MeasMap mapPitch, MeasMap mapYaw);
+  void setOpMode(int channel, int opMode);
+  void setRollKeyScale(int channel, int minNoteVal, int nrOfNotes, ScaleType type);
+  void setRollKeyScale(int channel, InstrType instr, ScaleType type);
+  void setPitchKeyScale(int channel, int minNoteVal, int nrOfNotes, ScaleType type);
+  void setPitchKeyScale(int channel, InstrType instr, ScaleType type);
+  void setYawKeyScale(int channel, int minNoteVal, int nrOfNotes, ScaleType type);
+  void setYawKeyScale(int channel, InstrType instr, ScaleType type);
 
   // -------------------------------------------------------------------------
   // Anwenderfunktionen
@@ -179,7 +253,7 @@ public:
   int resultRoll(int channel, MidiResultPtr refResult, float measValue);
   int resultPitch(int channel, MidiResultPtr refResult, float measValue);
   int resultYaw(int channel, MidiResultPtr refResult, float measValue);
-
+  int getOpMode(int channel);
 };
 
 //-----------------------------------------------------------------------------
diff --git a/libraries/MidiNotes/MidiNotes.cpp b/libraries/MidiNotes/MidiNotes.cpp
index ec91464f7411b1f25f33b3b536faf3b8dc1e8dcd..acb536154a0d26f45f22b0d9c8de9e8ca6617886 100644
--- a/libraries/MidiNotes/MidiNotes.cpp
+++ b/libraries/MidiNotes/MidiNotes.cpp
@@ -18,17 +18,38 @@
 // ----------------------------------------------------------------------------
 //
 
+int       MidiNotes::nrOfInstances = 0;
+MidiNotes *MidiNotes::InstArr[MaxNrOfInstances];
+int       MidiNotes::barDistanceValue;
+int       MidiNotes::barDistanceCount;
+int       MidiNotes::instLoopCount;
+int       MidiNotes::chnMap[MaxNrOfInstances];
+
+// Konstruktor
+//
+
+MidiNotes::MidiNotes() // @suppress("Class members should be properly initialized")
+{
+  InstArr[nrOfInstances] = this;
+  idxOfInstance = nrOfInstances;
+  chnMap[idxOfInstance] = -1;     // 0-15 sind verwendete Inhalte
+  nrOfInstances++;
+  instLoopCount = nrOfInstances;
+}
+
 void MidiNotes::begin(int inBpm, NoteDiv inRes, int inMidiCycle, IntrfBuf *inCRB)
 {
   dword       stdNoteMicroTick;
 
-  crb = inCRB;                        // Zeiger auf Ringpuffer
-  midiCycle = inMidiCycle;            // Zuklus der ZM in Mikrosekunden
-  bpm = inBpm;                        // Beats Per Minute (Metronom)
-  stdNoteMicroTick = 60000000 / bpm;  // Viertelnotenlänge in Mikrosekunden
+  crb = inCRB;                          // Zeiger auf Ringpuffer
+  midiCycle = inMidiCycle;              // Zuklus der ZM in Mikrosekunden
+  bpm = inBpm;                          // Beats Per Minute (Metronom)
+  stdNoteMicroTick = 60000000 / bpm;    // Viertelnotenlänge in Mikrosekunden
   stdNoteCount = stdNoteMicroTick / inMidiCycle;  // "  in Zyklen der ZM
-  stdNoteTick = 60000 / bpm;          // Viertelnotenlänge in Millisekunden
-  minNoteTick = stdNoteTick / inRes;  // Zu erwartende kürzeste Note (ms)
+  stdNoteTick = 60000 / bpm;            // Viertelnotenlänge in Millisekunden
+  minNoteTick = stdNoteTick / inRes;    // Zu erwartende kürzeste Note (ms)
+  minNoteCount = stdNoteCount / inRes;  //      "  in Zyklen der ZM
+  defaultPause = 20000 / inMidiCycle;   // Default Notenpause 20 ms
 
   typeList[nti0].length   = 8 * stdNoteCount;
   setNoteType(nti0);
@@ -141,11 +162,193 @@ int MidiNotes::addChordNote(NoteTypeIdx nti, byte val, byte vel)
   return(i);
 }
 
+// Auf einen (freien) Kanal schalten
+//
 void MidiNotes::setChannel(int chnVal)
 {
   if(chnVal < 1) chnVal = 1;
   if(chnVal > 16) chnVal = 16;
+
+  for(int i = 0; i < nrOfInstances; i++)
+    if(chnMap[i] == (chnVal - 1)) return;
+
+  oldChn = chn;
   chn = chnVal - 1;
+  chnMap[idxOfInstance] = chn;
+  newChannel = true;
+  next(smNewChannel);
+}
+
+// Auf den nächstgrößeren freien Kanal schalten
+//
+void MidiNotes::incChannel()
+{
+  int j;
+  int nextChn = chn;
+
+  for(int i = 0; i < 16; i++)
+  {
+    nextChn++;
+    if(nextChn > 15) nextChn = 0;
+    for(j = 1; j < nrOfInstances; j++)
+    {
+      if(chnMap[j] == nextChn) break;
+    }
+    if(j < nrOfInstances) continue;
+    else break;
+  }
+  if(j == 16) return;
+  oldChn = chn;
+  chn = nextChn;
+  chnMap[idxOfInstance] = chn;
+  newChannel = true;
+  next(smNewChannel);
+}
+
+void MidiNotes::incChannel(int min, int max)
+{
+  int j;
+  int nextChn = chn;
+
+  // TEST
+  if(debVarsPtr != NULL)
+  {
+    debVarsPtr->ByteArr[chn]++;
+    debVarsPtr->WordArr[0] = chnMap[0];
+    debVarsPtr->WordArr[1] = chnMap[1];
+    debVarsPtr->WordArr[2] = chnMap[2];
+  }
+
+  for(int i = 0; i < 16; i++)
+  {
+    nextChn++;
+    if(nextChn > (max - 1)) nextChn = min - 1;
+    for(j = 1; j < nrOfInstances; j++)
+    {
+      if(chnMap[j] == nextChn) break;
+    }
+    if(j < nrOfInstances) continue;
+    else break;
+  }
+  if(j == 16) return;
+
+  oldChn = chn;
+  chn = nextChn;
+  chnMap[idxOfInstance] = chn;
+  newChannel = true;
+  next(smNewChannel);
+
+  // TEST
+  if(debVarsPtr != NULL)
+  {
+    debVarsPtr->ByteArr[chn+4]++;
+    debVarsPtr->WordArr[4] = chnMap[0];
+    debVarsPtr->WordArr[5] = chnMap[1];
+    debVarsPtr->WordArr[6] = chnMap[2];
+    debVarsPtr->WordArr[7] = idxOfInstance;
+  }
+}
+
+// Auf den nächstkleineren freien Kanal schalten
+//
+void MidiNotes::decChannel()
+{
+  int j;
+  int nextChn = chn;
+
+  for(int i = 0; i < 16; i++)
+  {
+    nextChn--;
+    if(nextChn < 0) nextChn = 15;
+    for(j = 1; j < nrOfInstances; j++)
+    {
+      if(chnMap[j] == nextChn) break;
+    }
+    if(j < nrOfInstances) continue;
+    else break;
+  }
+  if(j == 16) return;
+  oldChn = chn;
+  chn = nextChn;
+  chnMap[idxOfInstance] = chn;
+  newChannel = true;
+  next(smNewChannel);
+}
+
+void MidiNotes::decChannel(int min, int max)
+{
+  int j;
+  int nextChn = chn;
+
+  for(int i = 0; i < 16; i++)
+  {
+    nextChn--;
+    if(nextChn < (min - 1)) nextChn = max - 1;
+    for(j = 1; j < nrOfInstances; j++)
+    {
+      if(chnMap[j] == nextChn) break;
+    }
+    if(j < nrOfInstances) continue;
+    else break;
+  }
+  if(j == 16) return;
+  oldChn = chn;
+  chn = nextChn;
+  chnMap[idxOfInstance] = chn;
+  newChannel = true;
+  next(smNewChannel);
+}
+
+// Verzögerung vor Einschalten in Mikrosekunden
+//
+void  MidiNotes::setNoteDelay(int delay)
+{
+  deltaNoteDelay = delay / midiCycle;
+}
+
+// Einstellung der Taktparameter
+//
+void MidiNotes::setBarParams(BarType bTyp)
+{
+  dword   distValueStdNote;
+
+  curBarType = bTyp;
+  distValueStdNote = nrOfInstances * stdNoteCount;
+
+  switch(bTyp)
+  {
+    case btZweiViertel:
+      barDistanceValue = 2 * distValueStdNote;
+      break;
+
+    case btDreiViertel:
+      barDistanceValue = 3 * distValueStdNote;
+      break;
+
+    case btVierViertel:
+      barDistanceValue = 4 * distValueStdNote;
+      break;
+
+    case btSechsViertel:
+      barDistanceValue = 6 * distValueStdNote;
+      break;
+
+    case btDreiAchtel:
+      barDistanceValue = distValueStdNote + distValueStdNote / 2;
+      break;
+
+    case btVierAchtel:
+      barDistanceValue = 2 * distValueStdNote;
+      break;
+
+    case btFuenfAchtel:
+      barDistanceValue = 2 * distValueStdNote + distValueStdNote / 2;
+      break;
+
+    case btSechsAchtel:
+      barDistanceValue = 3 * distValueStdNote;
+      break;
+  }
 }
 
 // ----------------------------------------------------------------------------
@@ -157,6 +360,11 @@ void MidiNotes::setOpMode(MidiOpMode mom)
   opMode = mom;
 }
 
+int MidiNotes::getOpMode()
+{
+  return(opMode);
+}
+
 
 void MidiNotes::setChordNote(int idx, NoteTypeIdx nti, int val, int vel)
 {
@@ -175,6 +383,45 @@ void MidiNotes::setChordNote(int idx, NoteTypeIdx nti, int val, int vel)
   newNote[idx].newVal = true;
 }
 
+void MidiNotes::setDeltaNote(int idx, byte val, byte vel)
+{
+  deltaNote[idx].oldValue   = deltaNote[idx].value;
+  deltaNote[idx].oldVeloc   = deltaNote[idx].veloc;
+  deltaNote[idx].value  = val;
+  deltaNote[idx].veloc  = vel;
+  deltaNote[idx].newVal = true;
+  lastDeltaIdx = idx;
+}
+
+void MidiNotes::setBarNote(NoteTypeIdx nti, byte val, byte vel)
+{
+  barNoteIn.value = val;
+  barNoteIn.veloc = vel;
+  barNoteIn.cycle = typeList[nti].length - defaultPause;
+  barNoteIn.pause = defaultPause;
+}
+
+void MidiNotes::opOff()
+{
+  stopOp = true;
+}
+
+void MidiNotes::opOn()
+{
+  stopOp = false;
+  stoppedOp = false;
+}
+
+void MidiNotes::saveOpMode()
+{
+  saveOpM = opMode;
+}
+
+void MidiNotes::reloadOpMode()
+{
+  opMode = saveOpM;
+  next(smRestart);
+}
 
 // ----------------------------------------------------------------------------
 // Steuerung, Zustandsmaschine
@@ -195,19 +442,37 @@ void MidiNotes::resume()
 void MidiNotes::run()
 {
   runCounter++;
+
   if(cycleCnt > 0) cycleCnt--;
 
+  // Synchronisierung aller Instanzen von MidiNotes auf den Takt
+  //
+  if(barDistanceCount > 0)
+  {
+    barDistanceCount--;
+  }
+  else
+  {
+    barDistanceCount = barDistanceValue;
+    for(int i = 0; i < nrOfInstances; i++)
+      InstArr[i]->newBar = true;
+    barCounter++;
+  }
+
   if(nextState != NULL)
     (this->*nextState)();
 }
 
 void MidiNotes::smInit()
 {
+  initCounter++;
   next(smIdle);
 }
 
 void MidiNotes::smIdle()
 {
+  idleCounter++;
+
   switch(opMode)
   {
     case momIdle:
@@ -218,11 +483,48 @@ void MidiNotes::smIdle()
       break;
 
     case momRunDelta:
+      next(smWaitDeltaNote);
+      break;
+
+    case momRunBar:
+      next(smStartBar);
       break;
   }
 }
 
+void MidiNotes::smRestart()
+{
+  int j = 0;
+
+  noteSeq[j++] = 0xB0 | chn;
+  noteSeq[j++] = 0x7B;        // Alle Noten ausschalten
+  noteSeq[j++] = 0;
+  crb->putSeq(noteSeq, j);
+
+  next(smIdle);
+}
+
+void MidiNotes::smNewChannel()
+{
+  int j = 0;
+
+  noteSeq[j++] = 0xB0 | oldChn;
+  noteSeq[j++] = 0x7B;        // Alle Noten ausschalten
+  noteSeq[j++] = 0;
+  crb->putSeq(noteSeq, j);
+
+  newChannel = false;
+
+  next(smIdle);
+}
+
+// ============================================================================
+// m o m S e q u e n c e
+// ============================================================================
+//
+// ---------------------------------------------
 void MidiNotes::smNoteOn()
+// ---------------------------------------------
 {
   int   i, j, tIdx;
   bool  doAttack;
@@ -234,6 +536,12 @@ void MidiNotes::smNoteOn()
     return;
   }
 
+  if(stopOp || stoppedOp)
+  {
+    stoppedOp = true;
+    return;
+  }
+
   if(crb == NULL)                   // Ohne Ausgabepuffer in Wartezustand
   {
     next(smIdle);
@@ -311,12 +619,16 @@ void MidiNotes::smNoteOn()
     next(smSustain);
 }
 
+// ---------------------------------------------
 void MidiNotes::smAttack()
+// ---------------------------------------------
 {
 
 }
 
+// ---------------------------------------------
 void MidiNotes::smDecay()
+// ---------------------------------------------
 {
 
 }
@@ -326,7 +638,9 @@ void MidiNotes::smDecay()
 // verarbeitet werden. Bei mehreren eingetragenen Noten würde die
 // kürzeste Note den Ablauf bestimmen.
 
+// ---------------------------------------------
 void MidiNotes::smSustain()
+// ---------------------------------------------
 {
   int   i;
   bool  sustFin;
@@ -348,12 +662,16 @@ void MidiNotes::smSustain()
     next(smNoteOff);
 }
 
+// ---------------------------------------------
 void MidiNotes::smRelease()
+// ---------------------------------------------
 {
 
 }
 
+// ---------------------------------------------
 void MidiNotes::smNoteOff()
+// ---------------------------------------------
 {
   int   i,j;
 
@@ -371,7 +689,7 @@ void MidiNotes::smNoteOff()
     }
 
     noteSeq[j++] = notePtr->value;    //Erste und weitere Noten liefern Liste  **
-    noteSeq[j++] = 0;
+    noteSeq[j++] = 64;
   }
 
   crb->putSeq(noteSeq, j);            // Sequenz an Puffer übergeben   *****
@@ -387,13 +705,234 @@ void MidiNotes::smPause()
     return;
   }
 
-  next(smNoteOn);
+  if(opMode != momSequence)
+    next(smIdle);
+  else
+    next(smNoteOn);
+}
+
+// ============================================================================
+// m o m R u n D e l t a
+// ============================================================================
+//
+void MidiNotes::smWaitDeltaNote()
+{
+  if((opMode != momRunDelta) || stopOp || stoppedOp)
+  {
+    stoppedOp = true;
+    next(smReleaseOldNote);
+    return;
+  }
+
+  if(!deltaNote[lastDeltaIdx].newVal) return;
+
+  next(smReleaseOldNote);
+}
+
+void MidiNotes::smReleaseOldNote()
+{
+  int j = 0;
+
+  if(deltaNote[lastDeltaIdx].oldValue != 0)
+  {
+    noteSeq[j++] = 0x80 | chn;
+    noteSeq[j++] = deltaNote[lastDeltaIdx].oldValue;
+    noteSeq[j++] = 0;
+    crb->putSeq(noteSeq, j);
+    deltaNote[lastDeltaIdx].oldValue = 0;
+  }
+
+  deltaNoteDelCnt = deltaNoteDelay;
+
+  if(opMode != momRunDelta)
+    next(smIdle);
+  else
+    next(smStartNewNote);
+
+  if(stopOp)
+  {
+    stoppedOp = true;
+    next(smOpStopped);
+  }
+}
+
+void MidiNotes::smStartNewNote()
+{
+  int j = 0;
+
+  if(stopOp)
+  {
+    stoppedOp = true;
+    next(smOpStopped);
+    return;
+  }
+
+  if(deltaNoteDelCnt > 0)
+  {
+    deltaNoteDelCnt--;
+    return;
+  }
+
+  noteSeq[j++] = 0x90 | chn;
+  noteSeq[j++] = deltaNote[lastDeltaIdx].value;
+  noteSeq[j++] = deltaNote[lastDeltaIdx].veloc;
+  crb->putSeq(noteSeq, j);
+
+  deltaNote[lastDeltaIdx].newVal = false;
+  next(smWaitDeltaNote);
+}
+
+void MidiNotes::smOpStopped()
+{
+  if(stoppedOp) return;
+  next(smWaitDeltaNote);
+}
+
+
+// ============================================================================
+// m o m C o n t r o l B a r
+// ============================================================================
+//
+
+
+// ----------------------------------------
+// Taktbeginn
+// ----------------------------------------
+void MidiNotes::smStartBar()
+{
+  int j = 0;
+
+  if(stopOp || stoppedOp)
+  {
+    stoppedOp = true;
+    return;
+  }
+
+  startBarCounter++;
+  if(debVarsPtr != NULL) debVarsPtr->DwordArr[0] = startBarCounter;
+
+  newBar = false;                   // Quittierung des Taktereignisses
+
+  noteSeq[j++] = 0x90 | chn;        // Midi Ton EIN
+  noteSeq[j++] = barNoteIn.value;
+  noteSeq[j++] = barNoteIn.veloc;
+  crb->putSeq(noteSeq, j);
+
+  // TEST
+  //if(debVarsPtr != NULL) debVarsPtr->ByteArr[0] = noteSeq[0];
+  //if(debVarsPtr != NULL) debVarsPtr->ByteArr[1] = noteSeq[1];
+  //if(debVarsPtr != NULL) debVarsPtr->ByteArr[2] = noteSeq[2];
+
+  barNoteCount = barNoteIn.cycle;   // Zeitzähler laden
+  barNotePause = barNoteIn.pause;
+
+  barNoteValue = barNoteIn.value;   // Merker für Notenwert
+  next(smRunNote);
+}
+
+// ----------------------------------------
+// Ablauf einer Note
+// ----------------------------------------
+void MidiNotes::smRunNote()
+{
+  runNoteCounter++;
+  if(debVarsPtr != NULL) debVarsPtr->DwordArr[1] = runNoteCounter;
+
+  if(newBar)
+  {
+    next(smStopNote);
+    return;
+  }
+
+  if(barNoteCount > 0)
+  {
+    barNoteCount--;
+    return;
+  }
+  next(smStopNote);
+}
+
+// ----------------------------------------
+// Ende, Note ausschalten
+// ----------------------------------------
+void MidiNotes::smStopNote()
+{
+  stopNoteCounter++;
+  if(debVarsPtr != NULL) debVarsPtr->DwordArr[2] = stopNoteCounter;
+
+  int j = 0;
+  noteSeq[j++] = 0x80 | chn;        // Midi Ton AUS
+  noteSeq[j++] = barNoteValue;
+  noteSeq[j++] = 0;
+  crb->putSeq(noteSeq, j);
+  next(smPauseNote);
+}
+
+// ----------------------------------------
+// Pause nach einer Note
+// ----------------------------------------
+void MidiNotes::smPauseNote()
+{
+  pauseNoteCounter++;
+  if(debVarsPtr != NULL) debVarsPtr->DwordArr[3] = pauseNoteCounter;
+
+  if(newBar)
+  {
+    next(smStartBar);
+    return;
+  }
+
+  if(barNotePause > 0)
+  {
+    barNotePause--;
+    return;
+  }
+
+  next(smStartNote);
+}
+
+// ----------------------------------------
+// Starten einer Note
+// ----------------------------------------
+void MidiNotes::smStartNote()
+{
+  if(stopOp || stoppedOp)
+  {
+    stoppedOp = true;
+    return;
+  }
+
+  startNoteCounter++;
+  if(debVarsPtr != NULL) debVarsPtr->DwordArr[4] = startNoteCounter;
+
+  if(newBar)
+  {
+    next(smStartBar);
+    return;
+  }
+
+  int j = 0;
+
+  noteSeq[j++] = 0x90 | chn;        // Midi Ton EIN
+  noteSeq[j++] = barNoteIn.value;
+  noteSeq[j++] = barNoteIn.veloc;
+  crb->putSeq(noteSeq, j);
+
+  barNoteCount = barNoteIn.cycle;   // Zeitzähler laden
+  barNotePause = barNoteIn.pause;
+
+  barNoteValue = barNoteIn.value;   // Merker für Notenwert
+  next(smRunNote);
 }
 
 // ----------------------------------------------------------------------------
 // Debugging
 // ----------------------------------------------------------------------------
 //
+void MidiNotes::setDebugVarsPtr(DebugVarsPtr ptr)
+{
+  debVarsPtr = ptr;
+}
 
 
 
diff --git a/libraries/MidiNotes/MidiNotes.h b/libraries/MidiNotes/MidiNotes.h
index 0f09fccd7d1a19c3c9171b0cf96c8320f5ad0b44..57e25bd4f813f270137d9d25ff5e7ecb29f94e45 100644
--- a/libraries/MidiNotes/MidiNotes.h
+++ b/libraries/MidiNotes/MidiNotes.h
@@ -16,14 +16,48 @@
 
 #include "arduinoDefs.h"
 #include "ComRingBuf.h"
+#include "IntrfDebug.h"
 
-#define MaxNrNoteSim    4
-#define MaxMidiSeq      (2 * MaxNrNoteSim + 1)
+/*
+#define DebugMidiNotes
 
-// Definierte Noten
+#ifdef DebugMidiNotes
+  #include "Monitor.h"
+#endif
+*/
+
+#define MaxNrNoteSim      4
+#define MaxMidiSeq        (2 * MaxNrNoteSim + 1)
+#define MaxNrOfInstances  16
+
+// Definierte Noten (Note 2, z.B. Roland)
 //
+#define NoteMin     0
+#define NoteC0      12
+#define NoteA0      21
+#define StartKB88   NoteA0
+#define NoteE1      28
+#define StartKB76   NoteE1
+#define NoteC2      36
+#define StartKB61   NoteC2
 #define SchlossC    60
 #define Kammerton   69
+#define NoteB6      95
+#define EndKB61     NoteB6
+#define NoteG7      103
+#define EndKB76     NoteG7
+#define NoteC8      108
+#define EndKB88     NoteC8
+#define NoteA7      117
+#define NoteMax     127
+
+typedef enum _InstrType
+{
+  itAnyType,
+  itKb61,
+  itKb76,
+  itKb88
+} InstrType;
 
 typedef enum  _NoteDiv
 {
@@ -38,7 +72,8 @@ typedef enum  _MidiOpMode
 {
   momIdle,
   momSequence,
-  momRunDelta
+  momRunDelta,
+  momRunBar,
 } MidiOpMode;
 
 
@@ -74,6 +109,19 @@ public:
     ntiNr
   } NoteTypeIdx;
 
+  typedef enum _BarType
+  {
+    btZweiViertel,
+    btDreiViertel,
+    btVierViertel,
+    btSechsViertel,
+    btDreiAchtel,
+    btVierAchtel,
+    btFuenfAchtel,
+    btSechsAchtel,
+    btNr
+  } BarType;
+
 
 private:
   // -------------------------------------------------------------------------
@@ -116,7 +164,25 @@ private:
     byte      typeIdx;
     byte      value;
     byte      veloc;
-  }NewNote;
+  } NewNote, *NewNotePtr;
+
+  typedef struct _DeltaNote
+  {
+    bool      newVal;
+    byte      value;
+    byte      oldValue;
+    byte      veloc;
+    byte      oldVeloc;
+  } DeltaNote, *DeltaNotePtr;
+
+  typedef struct _BarNote
+  {
+    byte      value;
+    byte      veloc;
+    dword     cycle;
+    dword     pause;
+    bool      linkToNext;
+  } BarNote, *BarNotePtr;
 
 #define NoteModeEmpty     0x00
 #define NoteModeRun       0x01
@@ -130,31 +196,73 @@ private:
   cbVector    nextState;
 
   MidiOpMode  opMode;
+  MidiOpMode  saveOpM;
 
   dword     runCounter;
+  dword     barCounter;
+  dword     initCounter;
+  dword     idleCounter;
+  dword     startBarCounter;
+  dword     runNoteCounter;
+  dword     stopNoteCounter;
+  dword     pauseNoteCounter;
+  dword     startNoteCounter;
+
   dword     cycleCnt;
 
   dword     midiCycle;      // Zustandstakt in Mikrosekunden
   dword     minNoteTick;    // minimale Notendauer in Millisekunden
+  dword     minNoteCount;   // minimale Notendauer in Zustandszyklen
   dword     bpm;            // Beats per Minute (Metronom)
   dword     stdNoteTick;    // Dauer einer Viertelnote in Millisekunden
   dword     stdNoteCount;   // Viertelnote in Zyklen der Zustandsmaschine
+  dword     defaultPause;   // Vorgesetzte Pause zwischen Noten (Count)
 
   Note      chord[MaxNrNoteSim];  // Liste der simultanen Noten (Akkord)
   NoteType  typeList[ntiNr];      // Liste der Notentypen
   byte      chn;                  // Aktueller Kanal
+  byte      oldChn;               // Vorheriger Kanal
+  bool      newChannel;           // Kennzeichnung neuer Kanal
   byte      noteSeq[MaxMidiSeq];  // Lokaler Telegrammaufbau
 
   NotePtr     notePtr;      // Temporäre Notendaten
   NoteTypePtr typePtr;      // Temporärer Notentyp
 
   dword     absPause;       // Pause für den zyklischen Ablauf
-  NewNote   newNote[MaxNrNoteSim];  // Übergabe neuer Noten
+  NewNote   newNote[MaxNrNoteSim];    // Übergabe neuer Noten
+
+  DeltaNote deltaNote[MaxNrNoteSim];  // Speicher für Notenänderung
+  int       lastDeltaIdx;
+  int       deltaNoteDelay;     // Verzögerung vor neuer Note
+  int       deltaNoteDelCnt;
+
+  BarNote   barNoteIn;          // Aktuell eingegangene Note
+  dword     barNoteCount;       // Zähler für Laufzeit der Note
+  dword     barNotePause;       // Zähler für Pause zwischen Noten
+  byte      barNoteValue;       // Merker für Notenwert
+  BarType   curBarType;
 
-  bool      stopRun;           // Anhalten der Midi-Schleife
-  bool      stoppedRun;        // Midi-Schleife angehalten
+  bool      stopRun;            // Anhalten der Midi-Schleife
+  bool      stoppedRun;         // Midi-Schleife angehalten
+  bool      stopOp;             // Anhalten des aktuellen Betriebs
+  bool      stoppedOp;          // Betrieb angehalten
 
+  bool      newBar;             // neuer Taktanfang
 
+public:
+  int       idxOfInstance;
+
+  // Statische Objekte für übergreifende Steuerungen
+  //
+  static int  nrOfInstances;
+  static MidiNotes *InstArr[MaxNrOfInstances];
+  static int  barDistanceValue;
+  static int  barDistanceCount;
+  static int  instLoopCount;
+
+  static int  chnMap[16];
+
+private:
   // --------------------------------------------------------------------------
   // Lokale Funktionen
   // --------------------------------------------------------------------------
@@ -164,7 +272,11 @@ private:
   // -----------------------------
   void smInit();
   void smIdle();
+  void smRestart();
+  void smNewChannel();
 
+  // momSequence
+  //
   void smNoteOn();
   void smAttack();
   void smDecay();
@@ -173,6 +285,20 @@ private:
   void smNoteOff();
   void smPause();
 
+  // momRunDelta
+  //
+  void smWaitDeltaNote();
+  void smReleaseOldNote();
+  void smStartNewNote();
+  void smOpStopped();
+
+  // momControlBar
+  //
+  void smStartBar();
+  void smRunNote();
+  void smStopNote();
+  void smPauseNote();
+  void smStartNote();
 
   // --------------------------------------------------------------------------
   // Inline-Funktionen
@@ -183,6 +309,11 @@ public:
   // --------------------------------------------------------------------------
   // Initialisierungen
   // --------------------------------------------------------------------------
+
+  // Konstruktor
+  //
+  MidiNotes();
+
   void begin(int inBpm, NoteDiv inRes, int inMidiCycle, IntrfBuf *inCRB);
 
 
@@ -197,14 +328,29 @@ public:
 
   int   addChordNote(NoteTypeIdx nti, byte val, byte vel);
 
-  void  setChannel(int chnVal);
+  void  setChannel(int chnVal);         // Setzen Midi-Kanal (1 bis 16)
+  void  incChannel();                   // Auf nächstgrößeren verfügbaren Kanal
+  void  incChannel(int min, int max);   // nächstgr. Kanal im Bereich
+  void  decChannel();                   // Auf nächstkleineren verfügbaren Kanal
+  void  decChannel(int min, int max);   // nächstkl. Kanal im Bereich
+
+  void  setNoteDelay(int delay);  // Verzögerung vor Einschalten in Mikrosekunden
+
+  void  setBarParams(BarType bTyp);   // Einstellen der Taktparameter
 
   // --------------------------------------------------------------------------
   // Betrieb
   // --------------------------------------------------------------------------
   //
   void setOpMode(MidiOpMode mom);
+  int  getOpMode();
   void setChordNote(int idx, NoteTypeIdx nti, int val, int vel);
+  void setDeltaNote(int idx, byte val, byte vel);
+  void setBarNote(NoteTypeIdx nti, byte val, byte vel);
+  void opOff();
+  void opOn();
+  void saveOpMode();
+  void reloadOpMode();
 
   // --------------------------------------------------------------------------
   // Steuerung, Zustandsmaschine
@@ -218,7 +364,8 @@ public:
   // Debugging
   // --------------------------------------------------------------------------
   //
-
+  DebugVarsPtr  debVarsPtr;
+  void setDebugVarsPtr(DebugVarsPtr ptr);
 };
 
 
diff --git a/sketches/SoaapBleMidiMaster/SoaapBleMidiMaster.h b/sketches/SoaapBleMidiMaster/SoaapBleMidiMaster.h
index 75ac85622fed5b354fc60c6fe6c8dc07e6968af5..2cb524b95f4e7666280afac82829021d3bb93611 100644
--- a/sketches/SoaapBleMidiMaster/SoaapBleMidiMaster.h
+++ b/sketches/SoaapBleMidiMaster/SoaapBleMidiMaster.h
@@ -17,6 +17,9 @@
 
 // Vordefinitionen, Festlegungen zur Kompilierung
 //
+#define IdxR1   1
+#define BdOffR  0
+#define BdSw1R  1
 
 //#define DebugTerminal
 // Mit dieser Definition werden die Klasse Monitor und weitere Testmethoden
@@ -34,10 +37,33 @@
 #include  "nRF52840Radio.h"
 #include  "MidiNotes.h"
 #include  "MeasMuse.h"
+#include  "MeasCtrl.h"
 #include  "BlePoll.h"
 #include  "ComRingBuf.h"
 #include  "nRF52840Ser.h"
+#include  "nRF52840Gpio.h"
+#include  "GpioCtrl.h"
 #include  "Monitor.h"
+#include  "IntrfDebug.h"
+
+char *infoThis =
+{
+"SOAAP BLE Master Midi Version 22.09.24-1\r\n"
+"c0  Abschalten der periodischen Meldung\r\n"
+"c1  Auslesen BlePoll-Debug-Register\r\n"
+"c2  Steuern/Analysieren des Polling\r\n"
+"c3  Werte auslesen\r\n"
+"c4  Anwendung analysieren\r\n"
+"c5  Meldung an Slave senden\r\n"
+};
+
+// Startmeldung (Version und drehender Strich)
+//
+char *StartMsg =
+{
+  "%@BleMidiMaster, Version 20231010-6 "
+};
+
 
 // ----------------------------------------------------------------------------
 // Vorwärtsreferenzen
@@ -55,6 +81,9 @@ void apTestController();
 void setParM1();
 void setParM2();
 void setParP1(int chn);
+void setParP2(int chn);
+void setParP3(int chn);
+void setParC1(int idx);
 
 
 #ifdef DebugTerminal
diff --git a/sketches/SoaapBleMidiMaster/SoaapBleMidiMaster.ino b/sketches/SoaapBleMidiMaster/SoaapBleMidiMaster.ino
index dd916b09eea5ed664401f3cc712ab8a9b420d8f4..cca6793d17c18e8757e630065281ab2ce75c6456 100644
--- a/sketches/SoaapBleMidiMaster/SoaapBleMidiMaster.ino
+++ b/sketches/SoaapBleMidiMaster/SoaapBleMidiMaster.ino
@@ -67,6 +67,9 @@ Value2Midi    val2midArr[NrOfSlavesToPoll + 1];
 MeasMuse    muse;
 // Ab atSOAAP2 werden die Konfigurationen in einer eigenen Klasse verwaltet
 
+MeasCtrl    ctrl;
+// Steuerfunktionen aus Messwerten abgeleitet
+
 SerParams     ttyParams;
 // ----------------------------------------------------------------------------
 nRF52840Ser   tty;
@@ -89,8 +92,18 @@ ComRingBuf    crb;
 // Die Klasse für die serielle Schnittstelle muss von <IntrfSerial> abgeleitet
 // sein, die Instanz wird mit der Funktion <begin(...)> übergeben.
 
-#define MidiCycle     1000
-MidiNotes   midi1,midi2;
+#define MidiCycleTimer  450
+#define MidiCycle       1350
+int   multMidiFirst = 1;
+int   multMidiLast = 4;
+int   multMidiSeq = 1;
+int   multMidiTasks = 3;
+
+// ----------------------------------------------------------------------------
+MidiNotes   midi[NrOfSlavesToPoll + 1];
+// ----------------------------------------------------------------------------
+// Jeder Slave bekommt seinen eigenen Midi-Treiber
+// Index 0 steht für eventuelle Midi-Aktivitäten des Masters
 
 #define appCycleTime 1000
 StateMachine  ap(apInit, NULL, appCycleTime);
@@ -104,6 +117,18 @@ StateMachine  ap(apInit, NULL, appCycleTime);
 //                aufgerufen wird.
 // <smCycleTime>  Die Zukluszeit (Takt) der Zustandsmaschine in Mikrosekunden
 
+GpioExtRef  LedGelb;
+// Handle für den Zugriff auf eine gelbe LED an Pin D11
+
+nRF52840Gpio  gpio;
+// Zugriff auf die digitalen Anschlüsse des Board
+
+#define GpioCycleTime 500
+// Zykluszeit (in Mikrosekunden) bei der Ansteuerung digitaler I/Os
+
+GpioCtrl  ioCtrl((IntrfGpio *) &gpio, GpioCycleTime);
+// Instanz zur Ansteuerung der Peripherie (Blinken, etc.)
+
 
 #ifdef DebugTerminal
 // ----------------------------------------------------------------------------
@@ -123,24 +148,6 @@ StateMachine  sm(smInit, NULL, smCycleTime);
 //                aufgerufen wird.
 // <smCycleTime>  Die Zukluszeit (Takt) der Zustandsmaschine in Millisekunden
 
-char *infoThis =
-{
-"SOAAP BLE Master Midi Version 22.09.24-1\r\n"
-"c0  Abschalten der periodischen Meldung\r\n"
-"c1  Auslesen BlePoll-Debug-Register\r\n"
-"c2  Steuern/Analysieren des Polling\r\n"
-"c3  Werte auslesen\r\n"
-"c4  Anwendung analysieren\r\n"
-"c5  Meldung an Slave senden\r\n"
-};
-
-// Startmeldung (Version und drehender Strich)
-//
-char *StartMsg =
-{
-  "%@BleMidiMaster, Version 20221117 "
-};
-
 char  runView[4] = {'|','/','-','\\'};
 int   runViewIdx = 0;
 
@@ -165,14 +172,23 @@ Monitor       mon(modeEcho | modeNl,0,&lc);
 
 BlePoll::AppType  appType;
 
+DebugVarsPtr  debVarsPtr;
+
 // ============================================================================
 void setup()
 // ============================================================================
 {
+  // Einschalten der gelben LED (am Pin D11 des Arduino-Board)
+  // bei der SOAAP-Anwendung
+  //
+  gpio.config(ArdD11, IfDrvOutput | IfDrvOpenDrain | IfDrvStrongLow, &LedGelb);
+  ioCtrl.blink(&LedGelb, 0, 2, 998, true);
+  //ioCtrl.blink(&LedRot, 0, 2, 200, 398, 2, true);
+
   bleCom.begin();           // Initialisierung der Datenübertragung
   //bleCom.setPower(0x08);     // Maximale Sendeleistung bei nRF52840
-  // TEST
-  //bleCom.setPower(0x0FC);   // Reduzierte Sendeleistung beim Schreibtisch-Test
+  // TEST 1
+  //bleCom.setPower(0x0FC);   // Reduzierte Sendeleistung beim Schreibtisch
   bleCom.setPower(0x000);   // Standard-Sendeleistung BLE
 
   //appType = BlePoll::atDevSOAAP;
@@ -206,15 +222,17 @@ void setup()
 #ifdef DebugTerminal
   blePoll.stopEP();       // Das Polling muss extra gestartet werden
   mon.setInfo(infoThis);  // Info-Meldung für Monitor
+  //midi[1].setDebugVarsPtr(mon.getDebVarsPtr());
+  debVarsPtr = mon.getDebVarsPtr();
 #endif
 
   // Initialisierung von serieller Schnittstelle und Ringpuffer
   // --------------------------------------------------------------------------
-  ttyParams.inst    = 0;    // Instanzindex der Schnittstelle (0,1)
-  ttyParams.rxdPort = 1;    // Nummer des IO-Port mit RxD-Pin
-  ttyParams.rxdPin  = 10;   // Nummer des RxD-Pin am Port
-  ttyParams.txdPort = 1;    // Nummer des IO-Port mit TxD-Pin
-  ttyParams.txdPin  = 3;    // Nummer des TxD-Pin am Port
+  ttyParams.inst    = SerCom1;  // Instanzindex der Schnittstelle (0,1)
+  ttyParams.rxdPort = 1;        // Nummer des IO-Port mit RxD-Pin
+  ttyParams.rxdPin  = 10;       // Nummer des RxD-Pin am Port
+  ttyParams.txdPort = 1;        // Nummer des IO-Port mit TxD-Pin
+  ttyParams.txdPin  = 3;        // Nummer des TxD-Pin am Port
   ttyParams.speed   = Baud31250;    // Enumerator für Bitrate
   ttyParams.type    = stCur;        // Stromschleife
 
@@ -233,14 +251,24 @@ void setup()
 
   setParM1();
   setParP1(1);
-  midi1.begin(120, nd32, MidiCycle, (IntrfBuf *) &crb);
-  midi1.setChannel(1);
+  midi[1].begin(120, nd32, MidiCycle, (IntrfBuf *) &crb);
+  midi[1].setChannel(1);
+  midi[1].setBarParams(MidiNotes::btVierViertel);
+  setParC1(1);
 
   setParM2();
-  setParP1(2);
-  midi2.begin(120, nd32, MidiCycle, (IntrfBuf *) &crb);
-  midi2.setChannel(2);
-  //midi2.stop();
+  setParP2(2);
+  midi[2].begin(120, nd32, MidiCycle, (IntrfBuf *) &crb);
+  midi[2].setChannel(6);
+  midi[2].setNoteDelay(5000);
+  setParC1(2);
+
+  setParP3(3);
+  midi[3].begin(120, nd32, MidiCycle, (IntrfBuf *) &crb);
+  midi[3].setChannel(11);
+  midi[3].setNoteDelay(5000);
+  setParC1(3);
+
 }
 
 // ============================================================================
@@ -267,22 +295,30 @@ void loop()
 
   // Alle <appCycleTime> Mikrosekunden erfolgt der Aufruf der Anwendung
   //
-  if(lc.timerMicro(lcTimer1, appCycleTime, 0, 10000))
+  if(lc.timerMicro(lcTimer1, appCycleTime, 0, 5000))
     ap.run();
 
-  // Alle <MidiCycle> Mikrosekunden erfolgt der Aufruf des Midi-Controller
+  // Alle <MidiCycleTimer> Mikrosekunden erfolgt der Aufruf eines Midi-Controller
   //
-  if(lc.timerMicro(lcTimer2, MidiCycle, 0, 1000))
+  if(lc.timerMicro(lcTimer2, MidiCycleTimer, 0, 1000))
   {
-    midi1.run();
-    midi2.run();
+    midi[multMidiSeq].run();
+    multMidiSeq++;
+    if(multMidiSeq >= multMidiLast)
+      multMidiSeq = multMidiFirst;
   }
 
+  // Alle 500 Mikrosekunden erfolgt der Aufruf der Peripheriesteuerung
+  //
+  if(lc.timerMicro(lcTimer3, GpioCycleTime, 0))
+    ioCtrl.run();
+
+
 #ifdef DebugTerminal
   // Jede halbe Sekunde erfolgt die Ausgabe einer Versionsmeldung
   // das kann über c0 am Terminal abgeschaltet werden
   //
-  if(lc.timerMilli(lcTimer3, 500, 0))
+  if(lc.timerMilli(lcTimer4, 500, 0))
   {
     if(!mon.cFlag[0])
     {
@@ -297,7 +333,7 @@ void loop()
 
   // Jede Millisekunde erfolgt der Aufruf der Zustandsmaschine
   //
-  if(lc.timerMilli(lcTimer4, smCycleTime, 0))
+  if(lc.timerMilli(lcTimer5, smCycleTime, 0))
   {
     sm.run();
   }
@@ -308,7 +344,11 @@ void loop()
 }
 
 // ----------------------------------------------------------------------------
-// Daten auf Midi-Noten abbilden
+// Daten auf Midi-Noten abbilden (Hilfsfunktionen)
+// ----------------------------------------------------------------------------
+
+// Grenzwerte für Rohwerte aus den Sensoren
+// Werden für SOAAP2 nicht verwendet
 //
 void setParM1()
 {
@@ -334,18 +374,78 @@ void setParM2()
   val2midArr[2].highNote = 96;
 }
 
+// Vordefinierte Parametrierungen für Winkelmesswerte (SOAAP2)
+//
 void setParP1(int chn)
 {
-
+  // Rollwinkelbereich, Offset, Minwert, Maxwert
   muse.setRollArea(chn, 90, 45, 170);
+
+  // Nickwinkelbereich, Offset, Minwert, Maxwert
   muse.setPitchArea(chn, 90, 45, 170);
 
+  // Zuweisung der Midisignale zu Roll-, Nick- und Gierwinkel
   muse.setAims(chn, NoteType, NoteVal, Nothing);
-  muse.setMidiArea(chn, NoteVal, 23, 96);
-  muse.setMidiArea(chn, NoteType, 2, 12);
+
+  // Midisignalbereiche, Signal, Minwert, Maxwert
+  //
+  muse.setMidiArea(chn, NoteVal, 0, 56);   // Notenwert (Höhe)
+  muse.setMidiArea(chn, NoteType, MidiNotes::nti1, MidiNotes::nti32);   // Notentyp (Länge)
+
+  // Einstellung der Abbildungsvorschrift Messwerte auf Midi
+  // für Rollwinkel, Nickwinkel und Gierwinkel
+  muse.setMapping(chn, BiLinear, Linear2Scale, BiLinear);
+
+  // Einstellen der Midi-Betriebsart
+  // (feste Notenlänge, Änderung der Länge, Taktausrichtung, etc.)
+  muse.setPitchKeyScale(chn, Tonic_C, 96, stMajor);
+  muse.setOpMode(chn, momRunBar);
+
+  // Voreinstellung für die erste Note
+  midi[1].setBarNote(MidiNotes::nti4, 40, 100);
+
+}
+
+void setParP2(int chn)
+{
+
+  muse.setRollArea(chn, 90, 45, 170);
+  //muse.setRollGap(chn, 0.2f);
+  muse.setPitchArea(chn, 90, 45, 170);
+  //muse.setPitchGap(chn, 0.75f);
+  muse.setAims(chn, NoteVel, NoteVal, Nothing);
+
+  muse.setMidiArea(chn, NoteVal, 18, 96);
+  muse.setMidiArea(chn, NoteVel, 1, 120);
+
   muse.setMapping(chn, BiLinear, BiLinear, BiLinear);
+  muse.setOpMode(chn, momRunDelta);
 }
 
+void setParP3(int chn)
+{
+
+  muse.setRollArea(chn, 90, 45, 170);
+  //muse.setRollGap(chn, 0.2f);
+  muse.setPitchArea(chn, 90, 45, 170);
+  //muse.setPitchGap(chn, 0.75f);
+  muse.setAims(chn, NoteVel, NoteVal, Nothing);
+
+  muse.setMidiArea(chn, NoteVal, 45, 78);
+  muse.setMidiArea(chn, NoteVel, 1, 120);
+
+  muse.setMapping(chn, BiLinear, BiLinear, BiLinear);
+  muse.setOpMode(chn, momRunDelta);
+}
+
+// Parameter für die Abbildung von Messwerten auf Steuerfunktionen setzen
+//
+void setParC1(int idx)
+{
+  ctrl.setDelta(idx, 2.0);      // Delta-Wert für Änderungserkennung
+  ctrl.setBand(idx, BdOffR, -95.0, -85.0);    // 90° Pos AUS
+  ctrl.setBand(idx, BdSw1R, -175.0, -100.0);   // Schalten
+}
 
 // ****************************************************************************
 // Z u s t a n d s m a s c h i n e   S O A A P - A n w e n d u n g  (ap)
@@ -366,6 +466,7 @@ PlpType   pAppId;                       // Anwendungs-Id aus Polling-Sicht
 
 int   lastNoteIdxM1 = 0;
 int   lastNoteIdxM2 = 0;
+int   lastNoteIdxM3 = 0;
 
 #ifdef TEST001
 char testMsgBuf[256];
@@ -378,12 +479,30 @@ void apInit()
 {
   apInitCnt++;
 
-  midi1.setNoteType(MidiNotes::nti8);
-  lastNoteIdxM1 = midi1.addChordNote(MidiNotes::nti8, SchlossC, 10);
-  midi1.setOpMode(momSequence);
-  midi2.setNoteType(MidiNotes::nti8);
-  lastNoteIdxM2 = midi2.addChordNote(MidiNotes::nti8, Kammerton, 10);
-  midi2.setOpMode(momSequence);
+  if(debVarsPtr != NULL)
+  {
+  for(int i = 0; i <= NrOfSlavesToPoll; i++)
+    debVarsPtr->ByteArr[i] = midi[i].idxOfInstance;
+  }
+  //midi1.setNoteType(MidiNotes::nti8);
+  midi[1].saveOpMode();
+  lastNoteIdxM1 = midi[1].addChordNote(MidiNotes::nti4, SchlossC, 10);
+  midi[1].setDeltaNote(lastNoteIdxM1, SchlossC, 20);
+  midi[1].setBarNote(MidiNotes::nti4, SchlossC, 20);
+  midi[1].setOpMode(momSequence);
+
+  //midi2.setNoteType(MidiNotes::nti8);
+  midi[2].saveOpMode();
+  lastNoteIdxM2 = midi[2].addChordNote(MidiNotes::nti4, Kammerton, 10);
+  midi[2].setDeltaNote(lastNoteIdxM2, Kammerton, 20);
+  midi[2].setBarNote(MidiNotes::nti4, Kammerton, 20);
+  midi[2].setOpMode(momSequence);
+
+  midi[3].saveOpMode();
+  lastNoteIdxM3 = midi[3].addChordNote(MidiNotes::nti4, Kammerton, 10);
+  midi[3].setDeltaNote(lastNoteIdxM2, Kammerton, 20);
+  midi[3].setBarNote(MidiNotes::nti4, Kammerton, 20);
+  midi[3].setOpMode(momSequence);
   ap.enter(apWaitDE);
 }
 
@@ -401,6 +520,13 @@ void apWaitDE()
   apNrOfSlaves = blePoll.getSlaveList(apSlaveList, NrOfSlavesToPoll);
   // Ermitteln der angeschlossenen Slaves
 
+
+  for(int i = 1; i < 4; i++)
+  {
+  midi[i].reloadOpMode();
+  ctrl.resetDeltaCount(i, BdOffR);
+  ctrl.resetDeltaCount(i, BdSw1R);
+  }
   ap.enter(apWaitMeas);
 }
 
@@ -424,6 +550,11 @@ void apWaitMeas()
   // Wenn kein Slave neue Messwerte hat,
   // dann im nächsten Zustandstakt Abfrage wiederholen
 
+
+  if(muse.getOpMode(snr) < 0) return;
+  // Wenn für den Slave keine Konfiguration vorliegt,
+  // dann wird er zur Zeit noch nicht weiter ausgewertet
+
   // Slave (curListIdx) hat Messwerte übermittelt
   // diese werden mit dem nächsten Takt verarbeitet
   ap.enter(apProcMeas);
@@ -444,25 +575,21 @@ void apProcMeas()
   pAppId = blePoll.getAppId(slNr);
   apNrOfMeasBytes = blePoll.getMeas(slNr, apMeasByteArray);
 
-  // Spezifisch je Slave auswerten
-  //
-  if(slNr == 1 || slNr == 2)
-  {
-    ap.enter(apCheckValues);
-    return;
-  }
-
-  // Auf nächsten Messwert von einem Slave warten
-  //
-  ap.enter(apWaitMeas);
+  ap.enter(apCheckValues);
 }
 
 // ----------------------------------------------------------------------------
-// Daten überprüfen (auswerten)
+// Daten überprüfen (auswerten, als Zahlen zwischenspeichern)
 //
 short accXold, accYold, accZold;
-float rollMeasValue, pitchMeasValue, yawMeasValue;
+float rollMeasValue[NrOfSlavesToPoll];
+float pitchMeasValue[NrOfSlavesToPoll];
+float yawMeasValue[NrOfSlavesToPoll];
 dword apCheckValuesCnt;
+bool  wasBdOffR;
+bool  wasBdSw1R;
+
+
 void apCheckValues()
 {
   apCheckValuesCnt++;
@@ -479,28 +606,32 @@ void apCheckValues()
      break;
 
    case BlePoll::atSOAAP2:
-     rollMeasValue  = * (float *) &apMeasByteArray[0];
-     pitchMeasValue = * (float *) &apMeasByteArray[4];
-     yawMeasValue   = * (float *) &apMeasByteArray[8];
+     rollMeasValue[slNr]  = * (float *) &apMeasByteArray[0];
+     pitchMeasValue[slNr] = * (float *) &apMeasByteArray[4];
+     yawMeasValue[slNr]   = * (float *) &apMeasByteArray[8];
 
+     ctrl.setBandCount(slNr, rollMeasValue[slNr]);
      break;
   }
 
   ap.enter(apCalcResult);
 }
 
+// ----------------------------------------------------------------------------
+// Daten abbilden, Ergebnisse
+//
 byte  resultAz;
 byte  resultAy;
 byte  resultAx;
 
-MidiResult  midiResult[3];
+MidiResult  midiResult[3][NrOfSlavesToPoll+1];
 
 dword apCalcResultCnt;
 void apCalcResult()
 {
   short     testY;
   int       result;
-  float     rollVal, pitchVal, yawVal;
+  //float     rollVal, pitchVal, yawVal;
   Meas2Midi meas2midi;
   bool      newResult;
 
@@ -533,49 +664,112 @@ void apCalcResult()
       break;
 
     case BlePoll::atSOAAP2:
-      result = muse.resultRoll(slNr, &midiResult[0], rollMeasValue);
+
+      // Midiwerte für das Rollen holen
+      //
+      result = muse.resultRoll(slNr, &midiResult[0][slNr], rollMeasValue[slNr]);
+
+      // TEST R1a
+      //if(debVarsPtr != NULL) debVarsPtr->DwordArr[0] = apCalcResultCnt;
+
       if(result < 0)
       {
+        // TEST R1a
+        //if(debVarsPtr != NULL) debVarsPtr->DwordArr[slNr]++;
+
+        // Schalten auf nächsten Kanal möglich
+        //
+        if(ctrl.getDeltaCount(slNr, BdOffR) > 3)
+        {
+          if(ctrl.getDeltaCount(slNr, BdSw1R) > 3)
+          {
+            ctrl.resetDeltaCount(slNr, BdOffR);
+            ctrl.resetDeltaCount(slNr, BdSw1R);
+            // TEST Switch
+            if(slNr == 1)
+              midi[slNr].incChannel(1,5);
+            else if(slNr == 2)
+              midi[slNr].incChannel(6,8);
+            else if(slNr == 3)
+              midi[slNr].incChannel(11,13);
+          }
+        }
+
         // Bereich verlassen (links)
         ap.enter(apWaitMeas);
+        midi[slNr].opOff();
         return;
       }
       else if(result > 0)
       {
+        // TEST R1a
+        //if(debVarsPtr != NULL) debVarsPtr->DwordArr[slNr+3]++;
+
         // Bereich verlassen (rechts)
         ap.enter(apWaitMeas);
+        midi[slNr].opOff();
         return;
       }
 
-      result = muse.resultPitch(slNr, &midiResult[1], pitchMeasValue);
+      // Midiwerte für das Nicken holen
+      //
+      result = muse.resultPitch(slNr, &midiResult[1][slNr], pitchMeasValue[slNr]);
       if(result < 0)
       {
         // Bereich verlassen (links)
         ap.enter(apWaitMeas);
+        midi[slNr].opOff();
         return;
       }
       else if(result > 0)
       {
         // Bereich verlassen (rechts)
         ap.enter(apWaitMeas);
+        midi[slNr].opOff();
         return;
       }
 
+      if(debVarsPtr != NULL) debVarsPtr->WordArr[slNr]++;
+
+      midi[slNr].opOn();
+      ctrl.resetDeltaCount(slNr, BdOffR);
+      ctrl.resetDeltaCount(slNr, BdSw1R);
+
+      // Midiwerte für das Gieren holen (Werte vorgeben)
+      //
+      if(muse.getOpMode(slNr) == momSequence)
+      {
+        midiResult[2][slNr].type    = NoteVel;
+        midiResult[2][slNr].value   = 100;
+        midiResult[2][slNr].newVal  = false;
+      }
+      else if(muse.getOpMode(slNr) == momRunDelta)
+      {
+        midiResult[2][slNr].type    = Nothing;
+        midiResult[2][slNr].value   = 0;
+        midiResult[2][slNr].newVal  = false;
+      }
+      else if(muse.getOpMode(slNr) == momRunBar)
+      {
+        midiResult[2][slNr].type    = NoteVel;
+        midiResult[2][slNr].value   = 100;
+        midiResult[2][slNr].newVal  = false;
+      }
+
 
-      midiResult[2].type  = NoteVel;
-      midiResult[2].value = 100;
       break;
   }
 
   newResult = false;
   for(int i = 0; i < 3; i++)
   {
-    if(midiResult[i].newVal)
+    if(midiResult[i][slNr].newVal)
     {
-      midiResult[i].newVal = false;
+      midiResult[i][slNr].newVal = false;
       newResult = true;
     }
   }
+
   if(newResult)
     ap.enter(apSetResult);
   else
@@ -586,37 +780,49 @@ void apCalcResult()
 // Bedienen des Midi-Controller
 //
 dword apSetResultCnt;
-byte noteTypeIn, noteValIn, noteVelIn;
+byte noteTypeIn[NrOfSlavesToPoll+1];
+byte noteValIn[NrOfSlavesToPoll+1];
+byte noteVelIn[NrOfSlavesToPoll+1];
 
 void apSetResult()
 {
+  int midiOpMode;
+
   apSetResultCnt++;
 
   switch (appType)
   {
     case BlePoll::atSOAAP1:
-      if(slNr == 1)
-        midi1.setChordNote(lastNoteIdxM1, (MidiNotes::NoteTypeIdx) resultAx, resultAz, 100);
-      else if(slNr == 2)
-        midi2.setChordNote(lastNoteIdxM2, (MidiNotes::NoteTypeIdx) resultAx, resultAz, 100);
+      midi[slNr].setChordNote(lastNoteIdxM1, (MidiNotes::NoteTypeIdx) resultAx, resultAz, 100);
       break;
 
     case BlePoll::atSOAAP2:
       for(int i = 0; i < 3; i++)
       {
-        if(midiResult[i].type == NoteType)
-          noteTypeIn = midiResult[i].value;
-        else if(midiResult[i].type == NoteVal)
-          noteValIn = midiResult[i].value;
-        else if(midiResult[i].type == NoteVel)
-          noteVelIn = midiResult[i].value;
+        if(midiResult[i][slNr].type == NoteType)
+          noteTypeIn[slNr] = midiResult[i][slNr].value;
+        else if(midiResult[i][slNr].type == NoteVal)
+          noteValIn[slNr] = midiResult[i][slNr].value;
+        else if(midiResult[i][slNr].type == NoteVel)
+          noteVelIn[slNr] = midiResult[i][slNr].value;
       }
 
+      midiOpMode = muse.getOpMode(slNr);
+      midi[slNr].setOpMode((MidiOpMode) midiOpMode);
+
+      if(midiOpMode == momSequence)
+      {
+        midi[slNr].setChordNote(lastNoteIdxM1,(MidiNotes::NoteTypeIdx) noteTypeIn[slNr], noteValIn[slNr], noteVelIn[slNr]);
+      }
+      else if(midiOpMode == momRunDelta)
+      {
+        midi[slNr].setDeltaNote(lastNoteIdxM2, noteValIn[slNr], noteVelIn[slNr]);
+      }
+      else if(midiOpMode == momRunBar)
+      {
+        midi[slNr].setBarNote((MidiNotes::NoteTypeIdx) noteTypeIn[slNr], noteValIn[slNr], noteVelIn[slNr]);
+      }
 
-      if(slNr == 1)
-        midi1.setChordNote(lastNoteIdxM1,(MidiNotes::NoteTypeIdx) noteTypeIn, noteValIn, noteVelIn);
-      else if(slNr == 2)
-        midi2.setChordNote(lastNoteIdxM1,(MidiNotes::NoteTypeIdx) noteTypeIn, noteValIn, noteVelIn);
       break;
   }
 
@@ -627,7 +833,7 @@ byte  testValue = 22;
 
 void apTestController()
 {
-  midi1.setChordNote(lastNoteIdxM1, MidiNotes::nti4, testValue, 60);
+  midi[1].setChordNote(lastNoteIdxM1, MidiNotes::nti4, testValue, 60);
   testValue++;
   if(testValue > 96)
     testValue = 22;
@@ -1042,20 +1248,21 @@ void smCheckApp()
   {
     mon.cprintln('m');
     mon.print(slNr); mon.cprint(' ');
-    mon.print(noteTypeIn); mon.cprint(' ');
-    mon.print(noteValIn); mon.cprint(' ');
-    mon.println(noteVelIn);
+    mon.print(midi[slNr].getOpMode()); mon.cprint(' ');
+    mon.print(noteTypeIn[slNr]); mon.cprint(' ');
+    mon.print(noteValIn[slNr]); mon.cprint(' ');
+    mon.println(noteVelIn[slNr]);
     sm.resetEnter();
   }
   else if(mon.lastKeyIn == 'V' || mon.lastKeyIn == 'v')
   {
     mon.cprintln('v');
     mon.print(slNr); mon.cprint(' ');
-    sprintf(conv,"r=%f",rollMeasValue);
+    sprintf(conv,"r=%f",rollMeasValue[slNr]);
     mon.print(conv); mon.cprint(' ');
-    sprintf(conv," p=%f",pitchMeasValue);
+    sprintf(conv," p=%f",pitchMeasValue[slNr]);
     mon.print(conv); mon.cprint(' ');
-    sprintf(conv," y=%f",yawMeasValue);
+    sprintf(conv," y=%f",yawMeasValue[slNr]);
     mon.println(conv);
     sm.resetEnter();
   }