Multitasking by ChrisMicro

From Hackteria Wiki
Revision as of 10:20, 4 May 2023 by Dusjagr (talk | contribs) (Created page with "/* recordAndPlaybackAtRate.ino What: This routine records a buttons pressed in a time series into the RAM memory and plays it afterwards in a loop. Why: This sof...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

/*

 recordAndPlaybackAtRate.ino
 What:
 This routine records a buttons pressed in a time series into the RAM memory
 and plays it afterwards in a loop.
 Why:
 This software is useful to controll leds ore motors in a repetitive manner.
 It can be used for music production, where you need repetitive beats.
 How:
 You need at least 2 buttons which are called "record" and "morse" button.
 Hold the "record" button and type in the series you want to play with the "morse" button.
 When you release the "record" button, the series is repeated on the actuator output.
 There a led ore other actuators can be connected.
 When you want to add new channels, take a look at the end of this code.
 Be aware: in the original version, the buttons where tight to 5V and external pull down
 resistors were used.
 In this version, the pullups in the microntroller are used and no external resistors are
 needed and the !!buttons have to be connected to GND!!
 Features of this version:
 In this version you have multiple record channels. For each channel you can add separate
 record buttons and separate actuator outputs.
 When you add more channels, more memory will be consumed. An Arduino Nano (Atmega328) has
 2K RAM which should be sufficient for 5 channels. If you want to have more channels or
 if you want to use a smaller processor you have to decrease the variable "maxSamples".
 With and Attiny85 two channels should be possible if you decrease "maxSamples" to 60.


 Hardware:
 - Arduino Nano
 - Buttons (connected to GND)
 - Actuators (LED with series resistor, active HIGH, connected to GND)


 2013-12-20 Owen Thomson (Mr Grok), committed b2d52c9
   https://bitbucket.org/mrgrok/mrgroks-arduino-sketches/src/b2d52c93640c9f582ac0cd0e867c2a67107274f4/recordAndPlaybackAtRate/recordAndPlaybackAtRate.ino?fileviewer=file-view-default
 2022-05-29 ChrisMicro, C++ Class Version for multiple loopers, description added


 Multitasking without delay by Adafruit:
 https://learn.adafruit.com/multi-tasking-the-arduino-part-1/a-classy-solution
  • /

// maximum number of samples // if you want to use more than 1 looper on an ATTINY85, // you have to decrease the number of samples, because the ATTINY85 RAM is only 512 bytes

  1. define maxSamples 100

class Looper {

   byte morseBtn;
   byte modeBtn;
   byte led; //led or motor control output
   const unsigned long sampleLength = 1;
   boolean inRecordMode = false;
   boolean wasInRecordMode = false;
   // buffers to record state/duration combinations
   boolean states[maxSamples];
   int durations[maxSamples];
   int currentSampleCycles;
   short idxPlayback = 0;
   short idxRecord = 0;
   unsigned long previousMillis;   // will store last time LED was updated
 private:
   void resetForRecording() {
     memset(states, 0, sizeof(states));
     memset(durations, 0, sizeof(durations));
     idxRecord = 0; // reset record idx just to make playback start point obvious
     idxPlayback = 0;
     currentSampleCycles = 0;
   }
   void recordLoop() {
     boolean state = (digitalRead(morseBtn) == LOW); // Low active input pin
     digitalWrite(led, state); // give feedback to person recording the loop
     if (states[idxRecord] == state) {
       // state not changed, add to duration of current state
       durations[idxRecord] += sampleLength;
     } else {
       // state changed, go to next idx and set default duration
       idxRecord++;
       if (idxRecord == maxSamples) {
         idxRecord = 0;  // reset idx if max array size reached
       }
       states[idxRecord] = state;
       durations[idxRecord] = sampleLength;
     }
     //  delay(sampleLength); // slow the loop to a time constant so we can reproduce the timelyness of the recording
   }
   void playbackLoop()
   {
     if (currentSampleCycles == 0 && durations[idxPlayback] != 0) {
       // state changed
       digitalWrite(led, states[idxPlayback]); // set led
     }
     if (idxPlayback == maxSamples) {
       // EOF recorded loop - repeat
       idxPlayback = 0;
       currentSampleCycles = 0;
     } else {
       //  if(durations[idxPlayback]*playbackMultiplier <= currentSampleCycles) // speed ctls deactivated
       if (durations[idxPlayback] * 1 <= currentSampleCycles)
       {
         // EOF current sample in recorder buffer
         idxPlayback++; // move to next sample
         currentSampleCycles = 0; // reset for next sample
       } else {
         // still in same sample, but in next cycle (==sampleLength approx)
         currentSampleCycles++;
       }
     }
     //delay(sampleLength); // keep time same as we had for record loop (approximately)
   }
 public:
   Looper( uint8_t modePin, uint8_t morsePin, uint8_t ledPin)
   {
     modeBtn = modePin;
     morseBtn = morsePin;
     led = ledPin;
     pinMode(modeBtn, INPUT_PULLUP); // use internal pullup, no need of external resistor
     pinMode(morseBtn, INPUT_PULLUP);
     pinMode(led, OUTPUT);
   }


   void update()
   {
     unsigned long currentMillis = millis();
     if (currentMillis - previousMillis >= sampleLength)
     {
       inRecordMode = (digitalRead(modeBtn) == LOW); // Low active input pin
       if (inRecordMode == true) {
         if (!wasInRecordMode) {
           resetForRecording();
         }
         recordLoop();
       } else {
         // continue playing loop
         playbackLoop();
       }
       wasInRecordMode = inRecordMode; // record prev state for next iteration so we know whether to reset the record arr index
     }
     previousMillis = currentMillis;  // Remember the time
   }

};

/**********************************************************************

  user section to add more channels
*********************************************************************/

//Looper( uint8_t modePin, uint8_t morsePin, uint8_t ledPin)

Looper channel1(4, 3, 13); Looper channel2(2, 3, 12);

// if you need more channels, add it here and in the main loop ("update")

void setup() {

}

void loop() {

 channel1.update();
 channel2.update();
 // add here more channels
 // don't forgett to define it above .. Looper channelx(modePin,morsePin,ledPin)

}