From 0e380521ccbeafd0a579c44cc917c1e5a9c3779a Mon Sep 17 00:00:00 2001 From: Ronaldo Pontes Date: Thu, 4 Apr 2019 18:44:05 +0100 Subject: [PATCH] Added BlockBOARD with 7x7 grid and touch control commands --- Factory Scripts/BlockBOARD.littlefoot | 1407 +++++++++++++++++ .../BlockBOARD.littlefootModes/MPE.mode | 29 + .../Multi Channel.mode | 29 + .../Single Channel.mode | 28 + 4 files changed, 1493 insertions(+) create mode 100644 Factory Scripts/BlockBOARD.littlefoot create mode 100644 Factory Scripts/BlockBOARD.littlefootModes/MPE.mode create mode 100644 Factory Scripts/BlockBOARD.littlefootModes/Multi Channel.mode create mode 100644 Factory Scripts/BlockBOARD.littlefootModes/Single Channel.mode diff --git a/Factory Scripts/BlockBOARD.littlefoot b/Factory Scripts/BlockBOARD.littlefoot new file mode 100644 index 0000000..cf9ee50 --- /dev/null +++ b/Factory Scripts/BlockBOARD.littlefoot @@ -0,0 +1,1407 @@ +/* + + +*/ + +#heapsize: 849 + +//============================================================================== +/* + Heap layout: + + === 7x7 = 49 Key Pad === + + 0-196 4 byte x 49 colours + + === 24 x Touch === + + 196 1 byte x 49 corresponding pad index (0xff if none) + 245 4 byte x 49 initial x positions (for relative pitchbend) + 441 4 byte x 49 initial y positions (for relative y axis) + 637 1 byte x 49 MIDI channel assigned + + === 16 x Channel === + + 686 1 byte x 16 touch to track for this channel (depends on tracking mode) + + === 24 x Touch === + + 702 1 byte x 49 Touch note number + 751 1 byte x 49 Touch velocity + 800 1 byte x 49 list of active touch indicies in order of first played to last played + + Parameters: + + // 0 MIDI Start Channel + // 1 MIDI End Channel + // 2 Use MPE + // 4 Octave + // 5 Transpose + // 6 Slide CC + // 7 Slide Mode + // 8 + // 9 + // 10 + // 11 + // 12 + // 13 + // 14 + // 15 Fixed Velocity + // 16 Fixed Velocity Value + // 17 Piano Mode + // 18 Glide Rate + // 19 Glidelock Enabled + // 20 Mode / GridSize + // 21 + // 22 Scale + // 23 Hide Mode + // 24 Chord + // 25 + // 26 + // 27 + // 28 + // 29 + // 30 Glide Tracking Mode + // 31 Pitch Bend Rate + // 32 Pressure Tracking Mode + +*/ +//============================================================================== + +const int purple = 0x5856d6; +const int blue = 0x007aff; +const int midblue = 0x34aadc; +const int lightblue = 0x5ad8fa; +const int green = 0x4cd964; +const int pink = 0xff2d55; +const int red = 0xff3b30; +const int orange = 0xff9500; +const int palegreen = 0xffcc0; + +const int gradientColour1 = 0x7199ff; +const int gradientColour2 = 0x6fe6ff; + +const int padHighColour = 0xff366CC5; +const int padLowColour = 0xffAA429A; + + +//============================================================================== + +int gridSize, padWidth, padSpacing; +int dimFactor, dimDelay, dimDelta; +int scaleBitmask; +int numNotesInScale; +int channelLastAssigned; +int activePads; +int pitchbendRange; +int octave; +int xShift, yShift; +int transpose; +int scale; // Major;Minor;Harmonic Minor;Pentatonic Neutral;Pentatonic Major;Pentatonic Minor;Blues;Dorian;Phrygian;Lydian;Mixolydian;Locrian;Whole Tone;Arabic (A);Arabic (B);Japanese;Ryukyu;8-Tone Spanish;Chromatic; +int chord; // Octave;Fifth;Major;Minor +bool hideMode; +int position; // Split into X and Y offsets +int clusterWidth; +int clusterHeight; +int xPos; +int yPos; +bool armTouchCommands; + +bool glideLockEnabled; +int glideLockInitialNote; +float glideLockTarget; +int glideLockChannel; + +bool gammaCorrected; + +//============================================================================== +void Pad_setColour (int padIndex, int colour) { setHeapInt ((padIndex * 4), colour); } +int Pad_getColour (int padIndex) { return getHeapInt (padIndex * 4); } +//void Pad_setNote (int padIndex, int note) { /* setHeapByte (padIndex + xxx, note); */} +//int Pad_getNote (int padIndex) { return getHeapByte (padIndex + xxx); } +void Pad_setActive (int padIndex, bool setActive) { activePads = setActive ? (activePads | (1 << padIndex)) : (activePads & ~(1 << padIndex)); } +bool Pad_isActive (int padIndex) { return activePads & (1 << padIndex); } +bool isAnyPadActive() { return activePads; } + +//============================================================================== +void Touch_setPad (int touchIndex, int padIndex) { setHeapByte (touchIndex + 196, padIndex); } +int Touch_getPad (int touchIndex) { return getHeapByte (touchIndex + 196); } +void Touch_setInitialX (int touchIndex, float initialX) { setHeapInt ((touchIndex * 4) + 245, int (initialX * 1e6)); } +float Touch_getInitialX (int touchIndex) { return float (getHeapInt ((touchIndex * 4) + 245)) / 1e6; } +void Touch_setInitialY (int touchIndex, float initialY) { setHeapInt ((touchIndex * 4) + 441, int (initialY * 1e6)); } +float Touch_getInitialY (int touchIndex) { return float (getHeapInt ((touchIndex * 4) + 441)) / 1e6; } +void Touch_setChannel (int touchIndex, int channel) { setHeapByte (touchIndex + 637, channel); } +int Touch_getChannel (int touchIndex) { return getHeapByte (touchIndex + 637); } +void Touch_setNote (int touchIndex, int noteNumber) { setHeapByte (touchIndex + 702, noteNumber); } +int Touch_getNote (int touchIndex) { return getHeapByte (touchIndex + 702); } +void Touch_setVelocity (int touchIndex, int velocity) { setHeapByte (touchIndex + 751, velocity); } +int Touch_getVelocity (int touchIndex) { return getHeapByte (touchIndex + 751); } +void Touch_setTouchByHistory (int touchIndex, int order){ setHeapByte (order + 800, touchIndex); } +int Touch_getTouchByHistory (int order) { return getHeapByte (order + 800); } + +//============================================================================== + +void Channel_setTrackedTouch (int channel, int touchIndex) +{ + setHeapByte (channel + 686, touchIndex); +} + +int Channel_getTrackedTouch (int channel) +{ + return getHeapByte (channel + 686); +} + +//============================================================================== +bool isPartOfScale (int noteRelativeToTonic) +{ + int noteAsBitSet = 0x01 << (mod (noteRelativeToTonic, 12)); + return (noteAsBitSet & scaleBitmask) != 0; +} + +int findNthNoteInScale (int n) +{ + int count = 0; + + for (int pos = 0; pos < 12; ++pos) + { + if (scaleBitmask & (0x01 << pos)) + { + if (count == n) + return pos; + + count++; + } + } + + return -1; +} + +//============================================================================== +int getTouchedPad (float x, float y) +{ + int col = int (x * 0.5 * float (gridSize)); + int row = int (y * 0.5 * float (gridSize)); + + return (gridSize * row) + col; +} + +//============================================================================== +int getNoteForPad (int padIndex) +{ + // convert pad index (starting top left) to index in note sequence (starting bottom left): + int lowestDefaultNote = 24; + int padRow = padIndex / gridSize; + int padCol = padIndex % gridSize; + int noteIndex = ((gridSize - 1) - padRow) * gridSize + padCol; + int topologyShift, scaleShift; + + //drums have a lower default octave to melodic + if (gridSize < 5) lowestDefaultNote = 36; + else if (gridSize <= 5) lowestDefaultNote = 48; + + int lowestNoteIndex = (octave * 12) + lowestDefaultNote + transpose; + + if (gridSize >= 5 ) + { + topologyShift = (xShift * 5) + (yShift * 25); + + if (! hideMode) + { + lowestNoteIndex += topologyShift; + } + else + { + lowestNoteIndex += roundDownDivide (topologyShift, numNotesInScale) * 12; + scaleShift = topologyShift % numNotesInScale; + + if (scaleShift < 0) + scaleShift += numNotesInScale; + + } + } + else + { + // Drum grid + int notesPerRow = getClusterWidth() * gridSize; + + return lowestNoteIndex + (xShift * gridSize) + padCol + (((gridSize - 1) - padRow) * notesPerRow) + (yShift * notesPerRow * gridSize); + } + + if (! hideMode) + return noteIndex + lowestNoteIndex; + + int note = findNthNoteInScale ((noteIndex + scaleShift) % numNotesInScale) + ((noteIndex + scaleShift) / numNotesInScale * 12) + lowestNoteIndex; + return note; +} + +int roundDownDivide (int a, int b) +{ + if (a >= 0) + return a / b; + else + return (a - b + 1) / b; +} + +//============================================================================== +int getTrailColour (int padColour) +{ + if (padColour == 0xff000000) + return 0xffaaaaaa; + + return blendARGB (0xFFFFFFFF, padColour); +} + +//============================================================================== +void updateDimFactor() +{ + int dimLim = gammaCorrected ? 100 : 180; + + if (isAnyPadActive() || dimDelta) + { + if (dimFactor < dimLim) + dimDelta = 60; + else + dimDelta = 0; + + dimFactor += dimDelta; + dimDelay = 3; + } + else + { + if (--dimDelay <= 0) + { + dimFactor -= 24; + + if (dimFactor < 0) + dimFactor = 0; + } + } +} + +//============================================================================== +void drawPads() +{ + int padIndex = 0; + + if (gridSize == 1) + drawGradient(padHighColour, padLowColour, 0, 0, 15, 15); + else + { + for (int padY = 0; padY < gridSize; ++padY) + { + for (int padX = 0; padX < gridSize; ++padX) + { + int overlayColour = Pad_isActive (padIndex) && gridSize > 1 ? 0x66ffffff : (dimFactor << 24); + + drawPad (padX, padY, blendARGB (Pad_getColour (padIndex), overlayColour), 0xb8); + + ++padIndex; + } + } + } + drawPadControls(); +} + +void drawPadControls() { + + if (!armTouchCommands) + fillPixel(red, 14, 0); + else + { + // Draw black border + fillRect (0, 0, 0, 15, 1); + fillRect (0, 0, 0, 1, 15); + fillRect (0, 14, 0, 1, 15); + fillRect (0, 0, 14, 15, 1); + + // Draw left controls + if (hideMode) fillRect (palegreen, 0, 1, 1, 2); + fillRect (lightblue, 0, 3, 1, 2); + fillRect (blue, 0, 5, 1, 2); + if (getLocalConfig(17)) fillRect (palegreen, 0, 7, 1, 2); + + // Draw armed button + fillPixel(green, 14, 0); + } + drawKeyControl(); + drawChordControl(); + fillRect (orange, 14, scale * 2 + 1, 1, 2); + +} + +void drawKeyControl() +{ + int keyControl = transpose; + if (keyControl == -1) keyControl = 12; + else if (keyControl == -3) keyControl = 10; + else if (keyControl > 4) keyControl+=1; + fillRect (orange, keyControl, 0, 2, 1); +} + +void drawChordControl() +{ + int chordControl = -2; + if (chord == 3) chordControl = 0; + else if (chord == 4) chordControl = 2; + else if (chord == 7) chordControl = 4; + else if (chord == 8) chordControl = 6; + else if (chord == 9) chordControl = 8; + else if (chord == 10) chordControl = 10; + else if (chord == 14) chordControl = 12; + fillRect (orange, chordControl, 14, 2, 1); +} + +void executeTouchCommand(int touchIndex, int x, int y, float z, float vz) +{ + //drawNumber(x, 0xffff0000, 1, 1); + //drawNumber(y, 0xffff0000, 2, 9); + + if (x == 193) { + if (y > 192) setLocalConfig(22, 6); // Blues + else if (y > 163) setLocalConfig(22, 5); // Pentatonic Minor + else if (y > 134) setLocalConfig(22, 4); // Pentatonic Major + else if (y > 106) setLocalConfig(22, 3); // Pentatonic Neutral + else if (y > 77) setLocalConfig(22, 2); // Harmonic Minor + else if (y > 34) setLocalConfig(22, 1); // Minor + else setLocalConfig(22, 0); // Major + } + else if (y == 193) { // CHORD: + if (x > 192) setLocalConfig(24, 1); // octave + else if (x > 172) setLocalConfig(24, 14); // m7b5 + else if (x > 149) setLocalConfig(24, 10); // 9th + else if (x > 120) setLocalConfig(24, 9); // Minor 7th + else if (x > 91) setLocalConfig(24, 8); // Major 7th + else if (x > 63) setLocalConfig(24, 7); // 7th + else if (x > 34) setLocalConfig(24, 4); // Minor + else setLocalConfig(24, 3); // Major + if (chord == getLocalConfig(24)) setLocalConfig(24, 0); + drawNumber(getLocalConfig(24), 0xffff0000, 2, 2); + } + else if (y < 8) { // KEY TRANSPOSE: + if (x > 172) setLocalConfig(5, -1); // B + else if (x > 149) setLocalConfig(5, -3); // A + else if (x > 120) setLocalConfig(5, 7); // G + else if (x > 91) setLocalConfig(5, 5); // F + else if (x > 63) setLocalConfig(5, 4); // E + else if (x > 34) setLocalConfig(5, 2); // D + else setLocalConfig(5, 0); // C + } + else if (x == 7) { + if (y > 134) {} + else if (y > 106) { + setLocalConfig(17, int(!getLocalConfig(17))); // Piano Mode + } else if (y > 77) { + setLocalConfig(4, octave - 1); + drawNumber(getLocalConfig(4), 0xffff0000, 2, 2); + } else if (y > 34) { + setLocalConfig(4, octave + 1); + drawNumber(getLocalConfig(4), 0xffff0000, 2, 2); + } + else setLocalConfig(23, int(!hideMode)); // Major + } +} + +bool drawGradient(int high, int low, int x, int y, int width, int height) +{ + int mid = blendARGB (high, low); + + int dimColour = (dimFactor << 24); + + high = blendARGB (high, dimColour); + mid = blendARGB (mid, dimColour); + low = blendARGB (low, dimColour); + + blendGradientRect (high, mid, low, mid, x, y, width, height); + + return true; +} + +void drawPad (int x, int y, int colour, int bottomRightCornerDarkeningAmount) +{ + int dark = blendARGB (colour, bottomRightCornerDarkeningAmount << 24); + int mid = blendARGB (colour, (bottomRightCornerDarkeningAmount / 2) << 24); + + int w = padWidth - padSpacing; + blendGradientRect (colour, mid, dark, mid, x * padWidth, y * padWidth, w, w); +} + +//============================================================================== +void initialiseScale() +{ + if (scale == 0) scaleBitmask = 0xab5; // major + else if (scale == 1) scaleBitmask = 0x5ad; // minor + else if (scale == 2) scaleBitmask = 0x9ad; // harmonic minor + else if (scale == 3) scaleBitmask = 0x4a5; // pentatonic neutral + else if (scale == 4) scaleBitmask = 0x295; // pentatonic major + else if (scale == 5) scaleBitmask = 0x4a9; // pentatomic minor + else if (scale == 6) scaleBitmask = 0x4e9; // blues + else if (scale == 7) scaleBitmask = 0x6ad; // dorian + else if (scale == 8) scaleBitmask = 0x5ab; // phrygian + else if (scale == 9) scaleBitmask = 0xad5; // lydian + else if (scale == 10) scaleBitmask = 0x6b5; // mixolydian + else if (scale == 11) scaleBitmask = 0x56b; // locrian + else if (scale == 12) scaleBitmask = 0x555; // whole tone + else if (scale == 13) scaleBitmask = 0xb6d; // arabic (A) + else if (scale == 14) scaleBitmask = 0x575; // arabic (B) + else if (scale == 15) scaleBitmask = 0x8d1; // japanese + else if (scale == 16) scaleBitmask = 0x8b1; // ryukyu + else if (scale == 17) scaleBitmask = 0x57b; // 8-tone spanish + else scaleBitmask = 0xfff; // chromatic + + int n = scaleBitmask; + n -= ((n >> 1) & 0x5555); + n = (((n >> 2) & 0x3333) + (n & 0x3333)); + n = (((n >> 4) + n) & 0x0f0f); + n += (n >> 8); + numNotesInScale = n & 0x3f; +} + +//============================================================================== +bool setDrumModePadColours() +{ + if (gridSize >= 4) + return false; + + int numPads = gridSize * gridSize; + + //for (int i = 0; i < numPads; ++i) + //{ + // int note = getNoteForPad (i); + // Pad_setNote (i, note); + //} + + if (gridSize == 1) + { + Pad_setColour (0, 0xff366CC5); + } + else + { + Pad_setColour (0, 0xffad64fb); + Pad_setColour (1, 0xff54e8fd); + Pad_setColour (2, 0xff3f89fb); + Pad_setColour (3, 0xff3c57fb); + } + return true; +} + +//============================================================================== +void initialisePads() +{ + setPitchCorrectionEnabled (gridSize >= 5); + + padWidth = 15 / gridSize; + padSpacing = gridSize > 1 ? (15 - gridSize * padWidth) / (gridSize - 1) : 0; + padWidth += padSpacing; + dimFactor = 0; + dimDelay = 12; + activePads = 0; + int numPads = gridSize * gridSize; + + + if (setDrumModePadColours()) + return; + + for (int padIndex = 0; padIndex < numPads; ++padIndex) + { + // note numbers: + int note = getNoteForPad (padIndex); + if (note < 0) note = 0; + + //Pad_setNote (padIndex, note); + + // pad colours: + int padColour = 0xffffffff; // tonic = white + + int noteInScale = mod (note - transpose, 12); + + if (noteInScale != 0) + { + // not the tonic! + + if (! hideMode && ! isPartOfScale (noteInScale)) + { + padColour = 0xff000000; + } + else + { + int blend = 0xff * (noteInScale - 1) / 10; + + padColour = blendARGB (gradientColour1 | 0xff000000, + (gradientColour2 & 0x00ffffff) | (blend << 24)); + } + } + + Pad_setColour (padIndex, padColour); + + float padSize = (2.0 / float (gridSize)); + float xStart = padSize * (padIndex % gridSize); + float yStart = padSize * (padIndex / gridSize); + + addPitchCorrectionPad (padIndex, note, xStart, yStart, padSize, padSize); + } +} + +//============================================================================== +void initialiseTouches() +{ + for (int touchIndex = 0; touchIndex < 24; ++touchIndex) + { + Touch_setPad (touchIndex, 0xff); + Touch_setChannel (touchIndex, 0xff); + Touch_setTouchByHistory (0xff, touchIndex); + } +} + +void initialiseChannels() +{ + for (int channel = 0; channel < 16; ++channel) + { + Channel_setTrackedTouch (channel, 0xff); + } +} + +void initialiseConfig() +{ + clusterWidth = 1; + clusterHeight = 1; + + setLocalConfigItemRange (4, -4, 6); + setLocalConfigItemRange (7, 0, 2); + setLocalConfigItemRange (20, 1, 7); + setLocalConfigItemRange (22, 0, 18); + setLocalConfigItemRange (24, 0, 14); + + setLocalConfigItemRange (30, 1, 4); + setLocalConfigItemRange (31, 1, 4); + + gridSize = getLocalConfig(20); + updateTopologyShift(); + + pitchbendRange = getLocalConfig(3); + octave = getLocalConfig(4); + scale = 0; + chord = 0; + hideMode = false; +} + +void initialiseGlideLock() +{ + glideLockEnabled = false; + glideLockInitialNote = 0; + glideLockTarget = 0.0; + glideLockChannel = 0; +} + +void initialise() +{ + setLocalConfigActiveState (0, true, true); + setLocalConfigActiveState (1, true, true); + setLocalConfigActiveState (2, true, true); + setLocalConfigActiveState (3, true, true); + setLocalConfigActiveState (4, true, true); + setLocalConfigActiveState (5, true, true); + setLocalConfigActiveState (6, true, true); + setLocalConfigActiveState (7, true, true); + setLocalConfigActiveState (10, true, true); + setLocalConfigActiveState (11, true, true); + setLocalConfigActiveState (12, true, true); + setLocalConfigActiveState (13, true, true); + setLocalConfigActiveState (14, true, true); + setLocalConfigActiveState (15, true, true); + setLocalConfigActiveState (16, true, true); + setLocalConfigActiveState (17, true, true); + setLocalConfigActiveState (18, true, true); + setLocalConfigActiveState (19, true, true); + setLocalConfigActiveState (20, true, true); + setLocalConfigActiveState (22, true, true); + setLocalConfigActiveState (23, true, true); + setLocalConfigActiveState (24, true, true); + setLocalConfigActiveState (30, true, true); + setLocalConfigActiveState (31, true, true); + setLocalConfigActiveState (32, true, true); + + // Enable gamma correction if supported on hardware + setLocalConfig (33, 1); + gammaCorrected = getLocalConfig (33) > 0; + + initialiseConfig(); + initialiseScale(); + initialisePads(); + initialiseTouches(); + initialiseChannels(); + initialiseGlideLock(); + + useMPEDuplicateFilter (true); +} + +void handleButtonDown (int index) +{ + if (index == 0) + { + ++gridSize; + + if (gridSize == 3) gridSize = 4; + if (gridSize == 8) gridSize = 1; + resetGrid(gridSize); + } +} + +void resetGrid(int gridSize) +{ + setLocalConfig (20, gridSize); + + initialiseScale(); + initialisePads (); + sendConfigItemToCluster (20); +} + +//============================================================================== +void repaint() +{ + clearDisplay(); + updateDimFactor(); + + if (isConnectedToHost()) + { + drawPads(); + } + + checkConfigUpdates(); + + // Overlay heatmap + drawPressureMap(); + fadePressureMap(); +} + +//============================================================================== +int getAbsPitch (int touchIndex, float x) +{ + float deltaX = (x - 1.0) * 12.0; + return getPitchWheelFromDeltaX (deltaX); +} + +int getPitchwheelValue (int touchIndex, float x) +{ + float initialX = Touch_getInitialX (touchIndex); + float scaler = (1.0 / (2.1 / float (gridSize))); + float deltaX = transformPitchForHideMode (touchIndex, scaler * (x - initialX)); + int correction = 0; + + if (gridSize >= 5) + correction = getPitchCorrectionPitchBend (touchIndex, getPitchWheelFromDeltaX (deltaX) - 8192); + + return getPitchWheelFromDeltaX (deltaX) + correction; +} + +int getPitchWheelFromDeltaX (float deltaX) +{ + deltaX = clamp (float (-getLocalConfig (3)), float (getLocalConfig (3)), deltaX); + + // now convert pitchbend in semitones to 14-bit pitchwheel position: + float pitchwheel = deltaX > 0.0 + ? map (deltaX, 0.0, float (getLocalConfig(3)), 8192.0, 16383.0) + : map (deltaX, float (-getLocalConfig(3)), 0.0, 0.0, 8192.0); + + return clamp (0, 16383, int (pitchwheel)); +} + +float transformPitchForHideMode (int touchIndex, float deltaX) +{ + if (! hideMode) + return deltaX; + + // interpolate between actual pitches of pads left and right to x + + int deltaXLeft = deltaX < 0 ? int (deltaX) - 1 : int (deltaX); + int initialPadIndex = Touch_getPad (touchIndex); + + int padIndexLeft = deltaXLeft + initialPadIndex; + int padIndexRight = padIndexLeft + 1; + + // rows are incrementing when going down, not up! + // if padIndexLeft/Right is outside of the edges of the block, you need + // to explicitly add/subtract two rows to compensate. + if (mod (padIndexLeft, gridSize) == gridSize - 1) + { + if (deltaX < 0) + padIndexLeft += 2 * gridSize; + + else if (deltaX > 0) + padIndexRight -= 2 * gridSize; + } + + float pitchLeft = getNoteForPad (padIndexLeft); + float pitchRight = getNoteForPad (padIndexRight); + + float deltaPitch = deltaX - float (deltaXLeft); + float pitch = (pitchLeft * (1 - deltaPitch)) + (pitchRight * deltaPitch); + + + //return pitch - float (Pad_getNote (initialPadIndex)); + return pitch - float (getNoteForPad (initialPadIndex)); +} + +//============================================================================== +int getYAxisValue (int touchIndex, float y) +{ + if (getLocalConfig (7) == 0) + return clamp (0, 127, int (127 - int (y * 63.5))); + + if (getLocalConfig (7) == 1) + return getYAxisBipolar (touchIndex, y); + + float yDelta = Touch_getInitialY (touchIndex) - y; + + y = 0.5 + (applyCurve (yDelta * 0.5)); + + return clamp (0, 127, int (y * 127)); +} + +int getYAxisBipolar (int touchIndex, float y) +{ + float yDelta = abs (y - Touch_getInitialY (touchIndex)); + + y = applyCurve (yDelta * 0.5); + + return clamp (0, 127, int (y * 127)); +} + +// Faster with lower value +float applyCurve (float yDelta) +{ + float scaler = float (getLocalConfig (12)) / 127.0; + + if (scaler > 0.0) + return yDelta / scaler; + else + return yDelta; +} + +//============================================================================== +void addTouchToList (int touchIndex) +{ + int endOfList = 0; + + while ((endOfList < 24) && (Touch_getTouchByHistory (endOfList) != 0xff)) + ++endOfList; + + if (endOfList < 24) + Touch_setTouchByHistory (touchIndex, endOfList); +} + +void deleteFromTouchList (int indexToDelete) +{ + for (int i = indexToDelete; i < 23; ++i) + { + if (Touch_getTouchByHistory (i) == 0xff) + return; + + Touch_setTouchByHistory (Touch_getTouchByHistory (i + 1), i); + } + + Touch_setTouchByHistory (0xff, 23); +} + +void removeTouchFromList (int touchIndex) +{ + for (int i = 0; i < 24; ++i) + { + int touch = Touch_getTouchByHistory (i); + + if (touch == 0xff) + return; + + if (touch == touchIndex) + { + deleteFromTouchList (i); + return; + } + } +} + +int getNumTouchesInList() +{ + int indexInList = 0; + + while (Touch_getTouchByHistory (indexInList) != 0xff && indexInList < 24) + ++indexInList; + + return indexInList; +} + +//============================================================================== +void resetGlideLockToNote (int note, int channel) +{ + glideLockInitialNote = note; + glideLockChannel = channel; + glideLockTarget = 8192.0; +} + +int getGlideLockRate() +{ + return int (map (float (getLocalConfig (18)), 0.0, 127.0, 16.0, 3000.0)); +} + +void setGlideLockTarget (int note) +{ + float delta = float (note - glideLockInitialNote); + glideLockTarget = getPitchWheelFromDeltaX (delta); + sendPitchBend (glideLockChannel, int (glideLockTarget), getGlideLockRate()); +} + +int getGlideLockDelta() +{ + return glideLockEnabled + ? int (map (glideLockTarget, 0.0, 16383.0, -8191.0, 8192.0)) + : 0; +} + +//============================================================================== + +void touchStart (int touchIndex, float x, float y, float z, float vz) +{ + int X = int (x*100); + int Y = int (y*100); + + if (Y < 8 && X > 192) + armTouchCommands = !armTouchCommands; + + else if (armTouchCommands && (X == 193 || X == 7 || Y == 7 || Y == 193)) + executeTouchCommand(touchIndex, X, Y, z, vz); + + else if (Touch_getPad (touchIndex) == 0xff) + playNote(touchIndex, x, y, z, vz); +} + +void playNote (int touchIndex, float x, float y, float z, float vz) +{ + + int padIndex = getTouchedPad (x, y); + //int note = clamp (0, 127, Pad_getNote (padIndex)); + int note = clamp (0, 127, getNoteForPad (padIndex)); + int colour = Pad_getColour (padIndex); + int channel = 0xff; + int velocity = clamp (1, 127, int (vz * 127.0)); + int pressure = clamp (0, 127, int (z * 127.0)); + bool glideLockConfigEnabled = getLocalConfig (19); + bool enableMidiNoteOn = true; + + addTouchToList (touchIndex); + + if (glideLockEnabled || (glideLockConfigEnabled && (gridSize > 1))) + { + if (! glideLockEnabled) + { + glideLockEnabled = true; + channel = assignChannel (note); + resetGlideLockToNote (note, channel); + } + else + { + channel = glideLockChannel; + setGlideLockTarget (note); + enableMidiNoteOn = false; + } + } + + if (channel == 0xff) + channel = assignChannel (note); + + Touch_setInitialY (touchIndex, y); + + if (enableMidiNoteOn) + { + if (pitchbendRange > 0) + { + if (gridSize == 1) + sendPitchBend (channel, getAbsPitch (touchIndex, x)); + else + sendPitchBend (channel, 8192); + } + + if (getLocalConfig (12)) + sendMIDI (0xb0 | channel, getLocalConfig (6), getYAxisValue (touchIndex, y)); + + sendMIDI (0xd0 | channel, pressure); + + sendNoteOn (channel, note, velocity); + } + + addPressurePoint (getTrailColour (colour), x, y, z * 32.0); + + Pad_setActive (padIndex, true); + + Touch_setPad (touchIndex, padIndex); + Touch_setNote (touchIndex, note); + Touch_setInitialX (touchIndex, x); + Touch_setChannel (touchIndex, channel); + Touch_setVelocity (touchIndex, velocity); + + Channel_setTrackedTouch (channel, touchIndex); +} + +void touchMove (int touchIndex, float x, float y, float z, float vz) +{ + + int padIndex = Touch_getPad (touchIndex); + + if (padIndex == 0xff) + return; // touch was not started. + + int channel = Touch_getChannel (touchIndex); + + if (Channel_getTrackedTouch (channel) != touchIndex) + return; // these are not the touch messages you're looking for... + + int note = Touch_getNote (touchIndex); + int pressure = clamp (0, 127, int (z * 127.0)); + + sendMIDI (0xd0 | channel, pressure); + + // Piano Mode acts as a fret + if (getLocalConfig (17)) + { + int newPadIndex = getTouchedPad (x, y); + //int newNote = clamp (0, 127, Pad_getNote (newPadIndex)); + int newNote = clamp (0, 127, getNoteForPad (newPadIndex)); + + if (note != newNote) + { + if (! glideLockEnabled) + { + sendNoteOff (channel, note, 0); + sendNoteOn (channel, newNote, Touch_getVelocity (touchIndex)); + } + else + { + setGlideLockTarget (newNote); + } + + Touch_setNote (touchIndex, newNote); + Touch_setPad (touchIndex, newPadIndex); + Pad_setActive (padIndex, false); + Pad_setActive (newPadIndex, true); + } + } + else + { + if (getLocalConfig (12)) + sendMIDI (0xb0 | channel, getLocalConfig (6), getYAxisValue (touchIndex, y)); + + if (pitchbendRange > 0) + { + int pitchwheelValue = 0; + + if (gridSize == 1) + pitchwheelValue = getAbsPitch (touchIndex, x); + else if (glideLockEnabled) + pitchwheelValue = getPitchwheelValue (touchIndex, x) + getGlideLockDelta(); + else + pitchwheelValue = getPitchwheelValue (touchIndex, x); + + + sendPitchBend (channel, clamp (0, 16383, pitchwheelValue)); + } + } + + addPressurePoint (getTrailColour (Pad_getColour (padIndex)), x, y, z * 32.0); +} + +void touchEnd (int touchIndex, float x, float y, float z, float vz) +{ + int padIndex = Touch_getPad (touchIndex); + + if (padIndex == 0xff) + return; // touch was not started. + + int channel = Touch_getChannel (touchIndex); + + int note = Touch_getNote (touchIndex); + int velocity = clamp (0, 127, int (vz * 127.0)); + + if (glideLockEnabled) + { + int numEvents = getNumTouchesInList(); + int eventNum = numEvents - 1; + + while (Touch_getTouchByHistory (eventNum) != touchIndex) + eventNum--; + + if (numEvents == 1) + { + glideLockEnabled = false; + sendPitchBend (glideLockChannel, 8192, 0); + sendNoteOff (glideLockChannel, glideLockInitialNote, velocity); + Channel_setTrackedTouch (glideLockChannel, 0xff); + deassignChannel (glideLockInitialNote, glideLockChannel); + } + else if (eventNum == (numEvents - 1)) + { + int previousTouch = Touch_getTouchByHistory (eventNum - 1); + int previousNote = Touch_getNote (previousTouch); + + setGlideLockTarget (previousNote); + Channel_setTrackedTouch (glideLockChannel, previousTouch); + Pad_setActive (Touch_getPad (previousTouch), true); + } + } + else + { + sendNoteOff (channel, note, velocity); + Channel_setTrackedTouch (channel, 0xff); + deassignChannel (note, channel); + } + + Pad_setActive (padIndex, false); + + Touch_setPad (touchIndex, 0xff); + Touch_setChannel (touchIndex, 0xff); + + removeTouchFromList (touchIndex); +} + +void updateTopologyShift () +{ + int xShiftLast = xShift; + int yShiftLast = yShift; + xShift = 0; + yShift = 0; + + + if (getClusterWidth() > 1) + { + if (gridSize < 5) + { + xShift = getClusterXpos(); + } + else if (isMasterInCurrentCluster()) + { + xShift = getHorizontalDistFromMaster() / 2; + } + else + { + int octStart = ((getClusterWidth() - 1) / 2); + xShift = (getClusterXpos() - octStart); + } + } + + if (getClusterHeight() > 1) + { + if (gridSize < 5) + { + yShift = getClusterYpos(); + } + if (isMasterInCurrentCluster()) + { + yShift = getVerticalDistFromMaster() / 2; + } + else + { + int octStart = ((getClusterHeight() - 1) / 2); + yShift = (getClusterYpos() - octStart); + } + } + + if (clusterWidth != getClusterWidth() || xPos != getClusterXpos() || xShiftLast != xShift) + { + if (isMasterInCurrentCluster()) + { + if (isMasterBlock()) + syncCluster(); + } + else if (! getClusterXpos()) + syncCluster(); + + initialiseScale(); + initialisePads(); + + clusterWidth = getClusterWidth(); + xPos = getClusterXpos(); + } + else if (clusterHeight != getClusterHeight() || yPos != getClusterYpos() || yShiftLast != yShift) + { + if (isMasterInCurrentCluster()) + { + if (isMasterBlock()) + syncCluster(); + } + else if (! getClusterYpos()) + syncCluster(); + + initialiseScale(); + initialisePads(); + + clusterHeight = getClusterHeight(); + yPos = getClusterYpos(); + } +} + +void syncCluster() +{ + if (getNumBlocksInCurrentCluster() < 2) + return; + + for (int i = 4; i <= 7; ++i) + sendConfigItemToCluster (i); + + for (int i = 10; i <= 23; ++i) + sendConfigItemToCluster (i); +} + +void sendConfigItemToCluster (int itemId) +{ + if (getNumBlocksInCurrentCluster() < 2) + return; + + int numBlocksInCluster = getNumBlocksInCurrentCluster(); + + for (int i = 0; i < numBlocksInCluster; ++i) + if (getBlockIdForBlockInCluster(i) != getBlockIDForIndex(0)) + setRemoteConfig (getBlockIdForBlockInCluster(i), itemId, getLocalConfig (itemId)); +} + +void checkConfigUpdates () +{ + if (scale != getLocalConfig (22)) + { + scale = getLocalConfig (22); + initialiseScale(); + initialisePads(); + } + if (chord != getLocalConfig (24)) + { + chord = getLocalConfig (24); + initialiseScale(); + initialisePads(); + } + if (gridSize != getLocalConfig (20)) + { + gridSize = getLocalConfig (20); + initialiseScale(); + initialisePads(); + } + if (octave != getLocalConfig (4)) + { + octave = getLocalConfig (4); + initialisePads(); + } + if (hideMode != getLocalConfig (23)) + { + hideMode = getLocalConfig (23); + initialiseScale(); + initialisePads(); + } + if (transpose != getLocalConfig (5)) + { + transpose = getLocalConfig (5); + initialisePads(); + } + + updateTopologyShift(); +} + +// Macros are 0-127 +int macro0Level; +int macro1Level; +int macro2Level; +int padXpos; +int padYpos; +int vSens; +int xSens; +int ySens; +int zSens; +int lSens; +int lastReqTime; + +void requestSensFromBlock (int address) +{ + if (getMillisecondCounter() > lastReqTime + 120) + { + sendMessageToBlock (address, 0x783C53D5, getBlockIDForIndex (0), 0); + lastReqTime = getMillisecondCounter(); + } +} + + +void handleMessage (int data0, int data1, int data2) +{ + // Sensitivities Response + if (data0 == 0x783E53D5) + { + vSens = (data1 >> 24) & 0x7F; + xSens = (data1 >> 16) & 0x7F; + ySens = (data1 >> 8) & 0x7F; + zSens = (data1 >> 0) & 0x7F; + lSens = (data2 >> 24) & 0x7F; + } +} + +const float padLowX = 0.12; +const float padHighX = 1.88; +const float padLowY = 0.12; +const float padHighY = 1.5; + +void handleXYTouch (int touchIndex, float x, float y, float z, bool newTouch) +{ + if (touchIndex > 1) + return; + + int newPadXpos = int (map (clamp (padLowX, padHighX, x), padLowX, padHighX, 0.0, 127.0)); + int newPadYpos = 127 - int (map (clamp (padLowY, padHighY, y), padLowY, padHighY, 0.0, 127.0)); + + if (padXpos != newPadXpos) + { + padXpos = newPadXpos; + sendCC (getControlChannel(), 113, padXpos); + } + if (padYpos != newPadYpos) + { + padYpos = newPadYpos; + sendCC (getControlChannel(), 114, padYpos); + } +} + +void drawXYPad() +{ + blendGradientRect (0xFF40C0B0, 0xff88f843, 0xFF40C0B0, 0xFF003060, 0, 0, 15, 12); + blendCircle (0xFFFFFFFF, map (float (padXpos), 0.0, 127.0, 0.0, 14.0), map (float (127 - padYpos), 0.0, 127.0, 0.0, 12.0), 1.4, true); +} + +void handleMIDI (int byte0, int byte1, int byte2) +{ + if ((byte0 & 0xF0) == 0xB0) + { + if (byte1 == 107) macro0Level = byte2; + if (byte1 == 109) macro1Level = byte2; + if (byte1 == 111) macro2Level = byte2; + if (byte1 == 113) padXpos = byte2; + if (byte1 == 114) padYpos = byte2; + } +} + + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +*/ diff --git a/Factory Scripts/BlockBOARD.littlefootModes/MPE.mode b/Factory Scripts/BlockBOARD.littlefootModes/MPE.mode new file mode 100644 index 0000000..1829968 --- /dev/null +++ b/Factory Scripts/BlockBOARD.littlefootModes/MPE.mode @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Factory Scripts/BlockBOARD.littlefootModes/Multi Channel.mode b/Factory Scripts/BlockBOARD.littlefootModes/Multi Channel.mode new file mode 100644 index 0000000..cda8888 --- /dev/null +++ b/Factory Scripts/BlockBOARD.littlefootModes/Multi Channel.mode @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Factory Scripts/BlockBOARD.littlefootModes/Single Channel.mode b/Factory Scripts/BlockBOARD.littlefootModes/Single Channel.mode new file mode 100644 index 0000000..eb1a9b6 --- /dev/null +++ b/Factory Scripts/BlockBOARD.littlefootModes/Single Channel.mode @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +