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(); }