#include #include #define TEMPO_ANALOG_IN 0 #define BARCOUNT_ANALOG_IN 1 #define STEPCOUNT_ANALOG_IN 2 #define MIDITHRU_PIN_IN 26 #define REVERSE_PIN_IN 22 #define SHIFT_PIN_IN 23 #define ERASE_PIN_IN 24 #define PLAYSTOP_PIN_IN 25 #define CHANNEL_1_LED 2 #define CHANNEL_2_LED 3 #define CHANNEL_3_LED 4 #define CHANNEL_4_LED 5 #define OUT_GATE 6 #define OUT_SYNC 7 #define CHANNEL_1_PIN 8 #define CHANNEL_2_PIN 28 #define CHANNEL_3_PIN 10 #define CHANNEL_4_PIN 11 #define BEATOUT_LED 12 #define CHANNEL_COUNT 4 #define STEP_PER_BAR_MAX 16 //def 16 #define TEMPO_MIN 30 #define TEMPO_MAX 200 #define MAX_MIDI_EV 920 #define MAX_PAS 768 //768 = 128 , 384 = 64 int sequence_length_max = MAX_PAS; byte ledPins[4] = {CHANNEL_1_LED, CHANNEL_2_LED, CHANNEL_3_LED, CHANNEL_4_LED}; MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); //------------------Tx 18 - Rx 19 ça évite l'interupteur à chaque upload bool shiftIsPressed = false; bool fillIsDone = false; bool debug = true; unsigned long delayStart = 0; bool delayIsRunning = false; int currentBarCount = 1; int currentStepCount = STEP_PER_BAR_MAX; uint32_t midiTick = 0; bool useMidiClock = false; int currentSeqLength = 16; bool isPlaying = false; bool isPlayingSwitchState = false; bool bypass = false; bool reverseMode = false; byte currentChannel = 0; byte currentPosition = 0; int compteurev = 0; byte mididatatype[MAX_MIDI_EV]; byte mididata1[MAX_MIDI_EV]; byte mididata2[MAX_MIDI_EV]; byte midichannel[MAX_MIDI_EV]; short midibar[MAX_MIDI_EV]; short pas = 1; short pastmp = sequence_length_max; int retard = 0; byte sensdl = 0; void clockOutput16PPQN(uint32_t *tick) { if (isPlaying) { bool isQuarterBeat = (((currentPosition % currentStepCount) % 4) == 0); digitalWrite(BEATOUT_LED, isQuarterBeat); currentPosition = (currentPosition + 1) % currentSeqLength; } else { digitalWrite(BEATOUT_LED, LOW); } } void clockOutput32PPQN(uint32_t *tick) { if (isPlaying) { digitalWrite(OUT_GATE, (*tick % 2) == 0 ? HIGH : LOW); digitalWrite(OUT_SYNC, (*tick % 4) < 2 ? HIGH : LOW); } else { digitalWrite(OUT_GATE, LOW); digitalWrite(OUT_SYNC, LOW); } } void clockOutput96PPQN(uint32_t *tick) { if (isPlaying) { MIDI.sendRealTime(midi::Clock); switch (sensdl) { case 0: pas++; if (pas >= currentSeqLength + 1) { pas = 1; } break; case 1: pas--; if (pas <= 0) { pas = currentSeqLength; } break; } } } void onStartClock() { MIDI.sendRealTime(midi::Start); } void onStopClock() { MIDI.sendRealTime(midi::Clock); } void setup() { TCCR1B = TCCR1B & B11111000 | B00000001; uClock.init(); uClock.setClock16PPQNOutput(clockOutput16PPQN); uClock.setClock32PPQNOutput(clockOutput32PPQN); uClock.setClock96PPQNOutput(clockOutput96PPQN); uClock.setOnClockStartOutput(onStartClock); uClock.setOnClockStopOutput(onStopClock); uClock.setTempo(96); uClock.start(); for (size_t channel = 0; channel < MAX_MIDI_EV; channel++) { mididatatype[channel] = 0; mididata1[channel] = 0; mididata2[channel] = 0; midichannel[channel] = 0; midibar[channel] = 0; } pinMode(CHANNEL_1_LED, OUTPUT); pinMode(CHANNEL_2_LED, OUTPUT); pinMode(CHANNEL_3_LED, OUTPUT); pinMode(CHANNEL_4_LED, OUTPUT); pinMode(CHANNEL_1_PIN, INPUT); pinMode(CHANNEL_2_PIN, INPUT); pinMode(CHANNEL_3_PIN, INPUT); pinMode(CHANNEL_4_PIN, INPUT); pinMode(MIDITHRU_PIN_IN, INPUT); pinMode(REVERSE_PIN_IN, INPUT); pinMode(SHIFT_PIN_IN, INPUT); pinMode(ERASE_PIN_IN, INPUT); pinMode(PLAYSTOP_PIN_IN, INPUT); pinMode(BEATOUT_LED, OUTPUT); pinMode(OUT_GATE, OUTPUT); pinMode(OUT_SYNC, OUTPUT); MIDI.setHandleNoteOn(handleNoteOn); MIDI.setHandleNoteOff(handleNoteOff); MIDI.setHandleControlChange(HandleControlChange); MIDI.setHandlePitchBend(HandlePitchBend); MIDI.setHandleProgramChange(HandleProgramChange); MIDI.setHandleStart(handleStart); MIDI.setHandleStop(handleStop); MIDI.setHandleClock(handleClock); MIDI.begin(MIDI_CHANNEL_OMNI); MIDI.turnThruOff(); } void handleShift() { int shiftState = digitalRead(SHIFT_PIN_IN); shiftIsPressed = shiftState; } void handleErase() { //-------------------------------------------------- ERASE current track bool erase = digitalRead(ERASE_PIN_IN); bool etatchange = false; int verif; if (erase >= true) { if (!shiftIsPressed) { for (size_t channel = 1; channel < MAX_MIDI_EV; channel++) { if (midichannel[channel] == currentChannel) { if (mididatatype[channel] == 1) { MIDI.sendNoteOff(mididata1[channel], 0, currentChannel + 1); } mididatatype[channel] = 0; mididata1[channel] = 0; mididata2[channel] = 0; midichannel[channel] = 0; midibar[channel] = 0; } } // ------------------------------------------ routine simple d'optimisation memoire for (size_t channel = 1; channel < MAX_MIDI_EV; channel++) { if (midibar[channel] == 0) { for (size_t nelcha = compteurev ; nelcha <= 1; nelcha--) { if (midibar[nelcha] > 0) { mididatatype[channel] = mididatatype[nelcha]; mididata1[channel] = mididata1[nelcha]; mididata2[channel] = mididata2[nelcha]; midichannel[channel] = midichannel[nelcha]; midibar[channel] = midibar[nelcha]; mididatatype[nelcha] = 0; mididata1[nelcha] = 0; mididata2[nelcha] = 0; midichannel[nelcha] = 0; midibar[nelcha] = 0; compteurev--; break; } } } if (channel >= compteurev) break; } etatchange = 1; // verif si 1 + while (etatchange != 0) { verif = compteurev + 1; if (midibar[verif] != 0) { compteurev++; etatchange = 1; } else { etatchange = 0; } } etatchange = 1; // verif si 0 - while (etatchange != 0) { verif = compteurev - 1; if (midibar[verif] == 0) { compteurev--; etatchange = 1; if (compteurev <= 0) { compteurev = 0; etatchange = 0; } } else { etatchange = 0; } } if (compteurev <= 0) compteurev = 0; if (compteurev > MAX_MIDI_EV) compteurev = MAX_MIDI_EV; } else { // ------------------------------------- Erase All for (size_t channel = 0; channel < MAX_MIDI_EV; channel++) { if (mididatatype[channel] == 1) { MIDI.sendNoteOff(mididata1[channel], 0, midichannel[channel] + 1); } mididatatype[channel] = 0; mididata1[channel] = 0; mididata2[channel] = 0; midichannel[channel] = 0; midibar[channel] = 0; } compteurev = 0; } } } void handleTempo() { int tempoPot = analogRead(TEMPO_ANALOG_IN); float tempo = round(((float)tempoPot / 1024.f) * (TEMPO_MAX - TEMPO_MIN) + TEMPO_MIN); uClock.setTempo(tempo); } void handleBarCount() { int pot = analogRead(BARCOUNT_ANALOG_IN); byte maxValue = sequence_length_max / STEP_PER_BAR_MAX; int nextBarCount; int valtmp = round(((float)pot / 1024.f) * (4 - 1) + 1); switch (valtmp) { case 1: nextBarCount = maxValue / 8; // 16 break; case 2: nextBarCount = maxValue / 4; // 32 break; case 3: nextBarCount = maxValue / 2; // 64 break; case 4: nextBarCount = maxValue; // 128 break; } if (nextBarCount != currentBarCount) { currentBarCount = nextBarCount; currentSeqLength = currentBarCount * currentStepCount; displayIntValue(valtmp); } } void handleStepCount() { int pot = analogRead(STEPCOUNT_ANALOG_IN); int nextStepCount; int valtmp = round(((float)pot / 1024.f) * (4 - 1) + 1); switch (valtmp) { case 1: nextStepCount = 4; break; case 2: nextStepCount = 4 * 2; break; case 3: nextStepCount = 4 * 3; break; case 4: nextStepCount = STEP_PER_BAR_MAX; break; } if (nextStepCount != currentStepCount) { currentStepCount = nextStepCount; currentSeqLength = currentBarCount * currentStepCount; displayIntValue(valtmp); } } void displayIntValue(int value) { for (int i = 0; i < CHANNEL_COUNT; i++) { if (value < i + 1) { digitalWrite(ledPins[i], LOW); } else { digitalWrite(ledPins[i], HIGH); } } delayStart = millis(); delayIsRunning = true; } void handleMidiThru() { int mlBypassState = digitalRead(MIDITHRU_PIN_IN) == HIGH; if (!isPlaying) { // Thru activé tout le temps sauf en lecture bypass = true; } else { bypass = mlBypassState; } } void handleReverse() { int reverseState = digitalRead(REVERSE_PIN_IN); if (reverseState != reverseMode) { reverseMode = reverseState; switch (reverseState) { case 0: sensdl = 0; retard = pas; if (retard % currentStepCount != 0) { retard /= currentStepCount; retard *= currentStepCount; retard += currentStepCount; } pas = pas + (pas - retard); break; case 1: sensdl = 1; retard = pas; if (retard % currentStepCount != 0) { retard /= currentStepCount; retard *= currentStepCount; retard += currentStepCount; } pas = pas - (pas - retard); break; } } } void handleCurrentChannel() { bool channelStates[4] = {false, false, false, false}; int presel = 0; channelStates[0] = digitalRead(CHANNEL_1_PIN) == HIGH; channelStates[1] = digitalRead(CHANNEL_2_PIN) == HIGH; channelStates[2] = digitalRead(CHANNEL_3_PIN) == HIGH; channelStates[3] = digitalRead(CHANNEL_4_PIN) == HIGH; if (shiftIsPressed) { for (byte i = 0; i < 4; i++) { if (channelStates[i]) { presel = i + 1; } } if (!fillIsDone && presel) { //-------------------- FILL MODE fill(presel); } for (byte i = 0; i < 4; i++) { if (channelStates[i] == 0 && presel == 0) { fillIsDone = false; } } } else { for (byte i = 0; i < 4; i++) { if (channelStates[i]) { currentChannel = i; } } } if (!delayIsRunning) { for (byte i = 0; i < CHANNEL_COUNT; i++) { bool state = i == (currentChannel); digitalWrite(ledPins[i], state ? HIGH : LOW); } } } void setIsPlaying(bool state) { isPlaying = state; if (state && !useMidiClock) { uClock.start(); } else { uClock.pause(); //currentPosition = 0; } } void handleStartStop() { //---------------------------------------------------------Start Stop int startStopStart = digitalRead(PLAYSTOP_PIN_IN) == HIGH; bool newPlayingState = startStopStart; if (newPlayingState != isPlayingSwitchState) { isPlayingSwitchState = newPlayingState; if (isPlayingSwitchState) { if (!isPlaying) { if (shiftIsPressed) // Play continu sur le pas suivant { currentPosition = 0; pas = pas + (pas - retard); } else { currentPosition = 0; pas = 1; } setIsPlaying(!isPlaying); } else { setIsPlaying(!isPlaying); for (size_t channel = 0; channel < MAX_MIDI_EV; channel++) { if (mididatatype[channel] == 1) { MIDI.sendNoteOff(mididata1[channel], 0, midichannel[channel] + 1); } } digitalWrite(BEATOUT_LED, LOW); retard = pas; if (retard % currentStepCount != 0) { retard /= currentStepCount; retard *= currentStepCount; retard += currentStepCount; } } } } } void fill(int presel) { for (size_t channel = 1; channel < MAX_MIDI_EV; channel++) { if (midichannel[channel] == currentChannel && midibar[channel] >= 1) { if (compteurev < MAX_MIDI_EV) { compteurev++; if (compteurev > MAX_MIDI_EV) compteurev = MAX_MIDI_EV; mididatatype[compteurev] = mididatatype[channel]; mididata1[compteurev] = mididata1[channel]; mididata2[compteurev] = mididata2[channel]; midichannel[compteurev] = presel - 1; midibar[compteurev] = midibar[channel]; } } } fillIsDone = true; } void loop() { //----------------------------------------------------------------------- Loop if (isPlaying) { if (pastmp != pas) { for (int channel = 0; channel < MAX_MIDI_EV; channel++) { //-------------------------------- Play Yvette if (midibar[channel] == pas) { switch (mididatatype[channel]) { case 0: MIDI.sendNoteOn(mididata1[channel], mididata2[channel], midichannel[channel] + 1); break; case 1: MIDI.sendNoteOff(mididata1[channel], mididata2[channel], midichannel[channel] + 1); break; case 2: MIDI.sendControlChange(mididata1[channel], mididata2[channel], midichannel[channel] + 1); break; case 3: int value = mididata1[channel] << 8 | mididata2[channel]; MIDI.sendPitchBend(value, midichannel[channel] + 1); break; } } } pastmp = pas; } } handleShift(); handleTempo(); handleBarCount(); handleStepCount(); handleMidiThru(); handleReverse(); handleStartStop(); handleCurrentChannel(); handleErase(); if ((millis() - delayStart) >= 2000 && delayIsRunning) { delayIsRunning = false; } MIDI.read(); } void handleNoteOn(byte channel, byte note, byte velocity) { if (bypass) { MIDI.sendNoteOn(note, velocity, currentChannel + 1); } else { if (compteurev <= MAX_MIDI_EV) { compteurev++; if (compteurev == 1) pas = 1; //------ remise au début du looper dès qu'une touche est détecté ;o) if (compteurev > MAX_MIDI_EV) compteurev = MAX_MIDI_EV; mididatatype[compteurev] = 0; mididata1[compteurev] = note; mididata2[compteurev] = velocity; midichannel[compteurev] = currentChannel; midibar[compteurev] = pas; if (pastmp == pas) MIDI.sendNoteOn(note, velocity, currentChannel + 1); //--- ne se lance que si la lecture du PAS est passé } } } void handleNoteOff(byte channel, byte note, byte velocity) { if (bypass) { MIDI.sendNoteOff(note, velocity, currentChannel + 1); } else { if (compteurev <= MAX_MIDI_EV) { compteurev++; if (compteurev > MAX_MIDI_EV) compteurev = MAX_MIDI_EV; mididatatype[compteurev] = 1; mididata1[compteurev] = note; mididata2[compteurev] = velocity; midichannel[compteurev] = currentChannel; midibar[compteurev] = pas; if (pastmp == pas) MIDI.sendNoteOff(note, velocity, currentChannel + 1); } } } void HandleControlChange(byte channel, byte number, byte value) { if (bypass) { MIDI.sendControlChange(number, value, currentChannel + 1); } else { if (compteurev <= MAX_MIDI_EV) { compteurev++; if (compteurev > MAX_MIDI_EV) compteurev = MAX_MIDI_EV; mididatatype[compteurev] = 2; mididata1[compteurev] = number; mididata2[compteurev] = value; midichannel[compteurev] = currentChannel; midibar[compteurev] = pas; if (pastmp == pas) MIDI.sendControlChange(number, value, currentChannel + 1); } } } void HandlePitchBend(byte channel, int value) { if (bypass) { MIDI.sendPitchBend(value, currentChannel + 1); } else { if (compteurev < MAX_MIDI_EV) { compteurev++; if (compteurev > MAX_MIDI_EV) compteurev = MAX_MIDI_EV; mididatatype[compteurev] = 3; mididata1[compteurev] = (byte)(value >> 8 & 0xff); mididata2[compteurev] = (byte)(value & 0xff); midichannel[compteurev] = currentChannel; midibar[compteurev] = pas; if (pastmp == pas) MIDI.sendPitchBend(value, currentChannel + 1); } } } void HandleProgramChange(byte channel, int value) { MIDI.sendProgramChange(value, channel); } void handleStart() { useMidiClock = true; setIsPlaying(true); midiTick = 0; } void handleStop() { setIsPlaying(false); useMidiClock = false; } void handleClock() { uClock.pause(); if (midiTick % 6 == 0) { uint32_t _step = midiTick / 6; clockOutput16PPQN(&_step); } if (midiTick % 3 == 0) { uint32_t half = midiTick / 3; clockOutput32PPQN(&half); } uint32_t full = midiTick; clockOutput96PPQN(&full); midiTick = midiTick + 1; }