├── README.md ├── Beat-Detection_test.ino ├── FastLED_Animation_Engine_Example_Skeleton.ino └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # FastLED-Animation-Engine 2 | A FastLED animation engine for running multiple animations simultaneously. Also provides techniques for writing advanced animations. 3 | -------------------------------------------------------------------------------- /Beat-Detection_test.ino: -------------------------------------------------------------------------------- 1 | //Work in progress sketch for live beat detection with fastLED 2 | //Uses multi threading and parallel processing 3 | //Core 0 runs an interrupt driven fft 4 | //Core 1 interprets the audio data and writes to the leds 5 | //The beat detection attempts to adjust itself based on the song but still could be improved 6 | //There is a section near the bottom you can uncomment if you want to see whats being analized on the serial plotter 7 | //Code is not particularly cleaned up but sharing anyway - You have been warned 8 | 9 | #include 10 | #define FASTLED_ALLOW_INTERRUPTS 1 11 | #include 12 | 13 | 14 | #define SAMPLES 1024 15 | #define SAMPLING_FREQ 40000 16 | #define NOISE 5000 17 | #define NUM_BANDS 16 18 | #define DATA_PIN 15 19 | #define COLOR_ORDER GRB 20 | #define LED_TYPE WS2812B 21 | #define BRIGHTNESS 96 22 | #define FRAMES_PER_SECOND 240 23 | #define NUM_LEDS 300 24 | 25 | CRGB leds[NUM_LEDS]; 26 | 27 | static hw_timer_t *timer = NULL; 28 | double localSamples[2][SAMPLES]; 29 | double localSamplesImaginary[2][SAMPLES]; 30 | 31 | SemaphoreHandle_t full_buffers; 32 | SemaphoreHandle_t empty_buffers; 33 | SemaphoreHandle_t ready_to_load; 34 | SemaphoreHandle_t ready_to_display; 35 | 36 | QueueHandle_t audioDataQueue; 37 | 38 | class bandVals{ 39 | public: 40 | int bandValues[16]; 41 | }; 42 | 43 | 44 | int16_t bufPos = 0; 45 | uint8_t sampleBuffer = 0; 46 | //Interrupt for sampling 47 | void IRAM_ATTR sampleInterrupt(){ 48 | BaseType_t xHigherPriorityTaskWoken = pdFALSE; 49 | //If current buffer full 50 | if(bufPos >= SAMPLES){ 51 | //Serial.println(bufPos); 52 | //Check if other buffer ready (Via semaphore) 53 | if(xSemaphoreTakeFromISR(empty_buffers, NULL)){//If ready give semaphore to let FFT know, switch to new buffer, counter = 0 54 | //Serial.println("taken"); 55 | if(!xSemaphoreGiveFromISR(full_buffers, &xHigherPriorityTaskWoken)){ 56 | Serial.println("Couldn't give full buffer");//This should never happen but checking just in case 57 | } 58 | sampleBuffer = (sampleBuffer + 1)%2; 59 | bufPos = 0; 60 | } 61 | else{//If not ready return 62 | //Serial.println("all buffers full"); 63 | return; 64 | } 65 | } 66 | //DO THE ACTUAL MEASUREMENT NOW OR YOUR FIRED 67 | localSamples[sampleBuffer][bufPos] = analogRead(36); 68 | //Serial.println(localSamples[sampleBuffer][bufPos]); 69 | localSamplesImaginary[sampleBuffer][bufPos] = 0; 70 | bufPos++; 71 | 72 | //make scheduler do thing 73 | if (xHigherPriorityTaskWoken){ 74 | portYIELD_FROM_ISR(); 75 | } 76 | } 77 | 78 | int FFTCount = 0; //Debugging variable 79 | 80 | //FFT Task 81 | void FFTLoop(void * param){ 82 | uint8_t index = 0;//Indicates which data buffer we will be doing the FFT on 83 | bandVals newBandVals; 84 | int bandValues[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // The length of these arrays must be >= NUM_BANDS 85 | arduinoFFT FFT[2] = {arduinoFFT(localSamples[0], localSamplesImaginary[0], SAMPLES, SAMPLING_FREQ), arduinoFFT(localSamples[1], localSamplesImaginary[1], SAMPLES, SAMPLING_FREQ)}; 86 | //Set up the ADC timer interrupt on core 0 87 | timer = timerBegin(0, 80, true); 88 | timerAttachInterrupt(timer, &sampleInterrupt, true); 89 | timerAlarmWrite(timer, 25, true); 90 | timerAlarmEnable(timer); 91 | 92 | while(1){ 93 | //Attempt to take a full buffer 94 | if(!xSemaphoreTake(full_buffers, 0)){//No buffers are ready to perform fft 95 | //Serial.println("No Buffers for fft"); 96 | continue; 97 | } 98 | //We have succesfully taken the semaphore, Begin FFT 99 | FFT[index].DCRemoval(); 100 | FFT[index].Windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD); 101 | FFT[index].Compute(FFT_FORWARD); 102 | FFT[index].ComplexToMagnitude(); 103 | for (int i = 0; i NOISE) { // Add a crude noise filter 108 | 109 | //16 bands, 12kHz top band 110 | if (i<=2 ) newBandVals.bandValues[0] += (int)localSamples[index][i]; 111 | if (i>2 && i<=3 ) newBandVals.bandValues[1] += (int)localSamples[index][i]; 112 | if (i>3 && i<=5 ) newBandVals.bandValues[2] += (int)localSamples[index][i]; 113 | if (i>5 && i<=7 ) newBandVals.bandValues[3] += (int)localSamples[index][i]; 114 | if (i>7 && i<=9 ) newBandVals.bandValues[4] += (int)localSamples[index][i]; 115 | if (i>9 && i<=13 ) newBandVals.bandValues[5] += (int)localSamples[index][i]; 116 | if (i>13 && i<=18 ) newBandVals.bandValues[6] += (int)localSamples[index][i]; 117 | if (i>18 && i<=25 ) newBandVals.bandValues[7] += (int)localSamples[index][i]; 118 | if (i>25 && i<=36 ) newBandVals.bandValues[8] += (int)localSamples[index][i]; 119 | if (i>36 && i<=50 ) newBandVals.bandValues[9] += (int)localSamples[index][i]; 120 | if (i>50 && i<=69 ) newBandVals.bandValues[10] += (int)localSamples[index][i]; 121 | if (i>69 && i<=97 ) newBandVals.bandValues[11] += (int)localSamples[index][i]; 122 | if (i>97 && i<=135) newBandVals.bandValues[12] += (int)localSamples[index][i]; 123 | if (i>135 && i<=189) newBandVals.bandValues[13] += (int)localSamples[index][i]; 124 | if (i>189 && i<=264) newBandVals.bandValues[14] += (int)localSamples[index][i]; 125 | if (i>264 ) newBandVals.bandValues[15] += (int)localSamples[index][i]; 126 | } 127 | } 128 | //Serial.println(bandValues[0]); 129 | //Done with the FFT, give an empty_buffer 130 | if(!xSemaphoreGive(empty_buffers)){ 131 | //This should never happen, but i've included it to test 132 | Serial.println("Could not give empty buff"); 133 | } 134 | //Update the index so next FFT is on the next buffer 135 | index = (index + 1)%2; 136 | 137 | //add band values to queue 138 | if (xQueueSend(audioDataQueue, (void *)&newBandVals, 0) != pdTRUE){ 139 | Serial.println("Queue Full"); 140 | } 141 | 142 | FFTCount++; 143 | } 144 | 145 | 146 | } 147 | 148 | int showCount = 0;//Debugging variable 149 | 150 | //Display LEDS task 151 | void displayLoop(void * param){ 152 | 153 | while(1){ 154 | //Wait until the CRGB array has been loaded 155 | xSemaphoreTake(ready_to_display, portMAX_DELAY); 156 | //Display the new leds 157 | FastLED.show(); 158 | showCount++; 159 | xSemaphoreGive(ready_to_load); 160 | 161 | //Delay this task until enough time has passed to keep a consistent frame rate 162 | vTaskDelay((1000/FRAMES_PER_SECOND)/portTICK_RATE_MS); 163 | } 164 | } 165 | 166 | //Beat detection and Writing LEDS task 167 | void writeLoop(void * param){ 168 | int pos = 0; 169 | float testF = 2.2; 170 | bool newAudio; 171 | bandVals bandValues; 172 | bandVals prevBandValues; 173 | int flux = 0; 174 | int realFlux = 0; 175 | int prevFlux = 0; 176 | int slowDecayPeak = 0; 177 | int fastDecayPeak = 0; 178 | int slowDecayMaxFlux = 0; 179 | int fastDecayMaxFlux = 0; 180 | int averagePosFlux = 0; 181 | int prevBandVal = 0; 182 | int bandVal = 0; 183 | int lastPart = 0; 184 | bool beat = true; 185 | bool fluxHasDecreased = true; 186 | bool partOn[10]; 187 | for(int x = 0; x < 10; x++){ 188 | partOn[x] = false; 189 | } 190 | 191 | while(1){ 192 | beat = false; 193 | if (xQueueReceive(audioDataQueue, (void *)&bandValues, 0) == pdTRUE){ 194 | newAudio = true; 195 | //Serial.println("rec"); 196 | }else{ 197 | xSemaphoreTake(ready_to_load, portMAX_DELAY); 198 | xSemaphoreGive(ready_to_display); 199 | continue; 200 | } 201 | 202 | bandVal = 0; 203 | 204 | //Trying something new -> combine the last two fourier transforms values 205 | //bandVal = bandValues.bandValues[0] + prevBandValues.bandValues[0]; 206 | 207 | prevBandValues.bandValues[0] = bandValues.bandValues[0]; 208 | 209 | bandVal = bandValues.bandValues[0]; 210 | 211 | realFlux = bandVal - prevBandVal; 212 | if(realFlux > 0){ 213 | flux = realFlux; 214 | averagePosFlux = (averagePosFlux*5 + 5*flux)/10; 215 | }else { 216 | flux = 0; 217 | averagePosFlux = (95*averagePosFlux)/100; 218 | } 219 | 220 | if(flux > fastDecayMaxFlux){ 221 | fastDecayMaxFlux = flux; 222 | if(fluxHasDecreased){ 223 | beat = true; 224 | } 225 | }else fastDecayMaxFlux = (fastDecayMaxFlux*8)/10; 226 | 227 | if(flux > slowDecayMaxFlux){ 228 | slowDecayMaxFlux = flux; 229 | }else { 230 | if(flux == 0){ 231 | slowDecayMaxFlux = slowDecayMaxFlux*995/1000; 232 | }else{ 233 | slowDecayMaxFlux = (slowDecayMaxFlux*999 + flux)/1000; 234 | } 235 | } 236 | 237 | if(bandVal > fastDecayPeak){ 238 | fastDecayPeak = bandVal; 239 | }else fastDecayPeak = (fastDecayPeak*9)/10; 240 | 241 | if(bandVal > slowDecayPeak){ 242 | slowDecayPeak = bandVal; 243 | }else slowDecayPeak = (slowDecayPeak*999 + bandVal)/1000; 244 | 245 | if(fastDecayMaxFlux < slowDecayMaxFlux*3/8){ 246 | beat = false; 247 | } 248 | 249 | if(fastDecayMaxFlux < averagePosFlux || flux == 0){ 250 | beat = false; 251 | } 252 | 253 | if(flux < prevFlux){ 254 | fluxHasDecreased = true; 255 | } 256 | prevFlux = flux; 257 | prevBandVal = bandVal; 258 | 259 | if(flux > 0){ 260 | //beat = true; 261 | } 262 | 263 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~Uncomment this section to see results in serial monitor~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 264 | /* 265 | Serial.print(flux); 266 | Serial.print(","); 267 | Serial.print(fastDecayMaxFlux); 268 | Serial.print(","); 269 | Serial.print(slowDecayMaxFlux*3/8); 270 | Serial.print(","); 271 | Serial.println(averagePosFlux); 272 | */ 273 | 274 | 275 | //Serial.print(slowDecayMaxFlux/2); 276 | 277 | //All writing to leds should be done wihtin this critical section 278 | xSemaphoreTake(ready_to_load, portMAX_DELAY); 279 | fadeToBlackBy(leds, NUM_LEDS, 255); 280 | 281 | if(beat){ 282 | fluxHasDecreased = false; 283 | int ran = random(0, 10); 284 | if(lastPart == ran){ 285 | ran = (ran + 1)%10; 286 | } 287 | lastPart = ran; 288 | if(partOn[ran]){ 289 | partOn[ran] = false; 290 | }else partOn[ran] = true; 291 | } 292 | for(int x = 0; x < 10; x++){ 293 | if(partOn[x]){ 294 | for(int i = 0; i < 10; i++){ 295 | leds[x*10 + i] = CHSV(255,255,255); 296 | } 297 | } 298 | } 299 | 300 | xSemaphoreGive(ready_to_display); 301 | } 302 | } 303 | 304 | void setup() { 305 | // put your setup code here, to run once: 306 | delay(1000); 307 | FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); 308 | FastLED.setBrightness(BRIGHTNESS); 309 | Serial.begin(115200); 310 | full_buffers = xSemaphoreCreateCounting(1, 0); 311 | empty_buffers = xSemaphoreCreateCounting(1, 1); 312 | ready_to_display = xSemaphoreCreateCounting(1, 0); 313 | ready_to_load = xSemaphoreCreateCounting(1, 1); 314 | audioDataQueue = xQueueCreate(1, sizeof(bandVals)); 315 | disableCore0WDT(); 316 | xTaskCreatePinnedToCore(FFTLoop, "FFT Task", 10000, NULL, configMAX_PRIORITIES - 1, NULL, 0); 317 | xTaskCreatePinnedToCore(displayLoop, "display Task", 10000, NULL, configMAX_PRIORITIES - 1 , NULL, 1); 318 | xTaskCreatePinnedToCore(writeLoop, "write Task", 10000, NULL, 5, NULL, 1); 319 | } 320 | 321 | void loop() {//We don't use the loop, everything on its own task 322 | vTaskDelay(10000000/portTICK_RATE_MS); 323 | /* 324 | Serial.print("NUM FFT: "); 325 | Serial.println(FFTCount); 326 | Serial.print("NUM WRITE: "); 327 | Serial.println(showCount); 328 | */ 329 | } 330 | -------------------------------------------------------------------------------- /FastLED_Animation_Engine_Example_Skeleton.ino: -------------------------------------------------------------------------------- 1 | // Animation Engine Example Skeleton 2 | // This sketch shows a basic method for running multiple instances of multiple animations all on the same strip without using any mallocs or frees on a microcontroller. 3 | // I have found many ways to improve upon this concept, but I attempted to keep this sketch simple so that it may serve as a starting point for others and hopefully 4 | // isn't too overwhelming for those with at least some coding experience. 5 | // To fully understand HOW this sketch works requires some coding knowledge, but if you just want to use it and add your own animations it shouldn't be too bad. 6 | // The "meat" of this sketch is an array of animation objects. Each animation object has it's own function pointer which points to a function that will actually 7 | // perform the calculation for the animation and write to the CRGB array. 8 | // Every cycle, the controller will iterate through that array and play all the animations inside of it. 9 | // In order to allow multiple instances of the same animations to play simultaneously without needing to dynamically create new objects during runtime, the variables 10 | // each animation uses are stored in a seperate array. 11 | // Each animation function takes a single integer as an input parameter. 12 | // This input parameter tells the animation where it's variables are stored as well as where it is located in the animation array. 13 | // I have included example animations written at the end of this sketch. Some of these animations use "Advanced techniques" such as automatically creating and deleting animations 14 | // To add your own animations, write the animation function in the same manner as the examples at the bottom of the sketch and declare a new animation object containing 15 | // the name of the animation, the corresponding function, the variable names, and how many variables are needed 16 | // I may continue to add more to this if it gains enough interest. 17 | // Future improvements I could add include: Accompanying bluetooth Android app, Audio Sampling and FFT for music reactive animations, Support for color palletes, 18 | // animation variable memory management improvements, unique loading techniques for different animations, and more advanced animation writing techniques. 19 | // Garrick Hogrebe https://www.linkedin.com/in/garrick-hogrebe-321043180/ 20 | 21 | #include 22 | FASTLED_USING_NAMESPACE 23 | 24 | #define DATA_PIN 15 25 | #define LED_TYPE WS2812B 26 | #define COLOR_ORDER GRB 27 | #define NUM_LEDS 100 28 | CRGB leds[NUM_LEDS]; 29 | 30 | #define MAX_ANIMATIONS 20 31 | #define VARIABLES_PER_ANIMATION 7 32 | 33 | #define BRIGHTNESS 100 34 | #define FRAMES_PER_SECOND 120 35 | 36 | 37 | 38 | // Some class definitions. In this sketch, these classes are only really neccesary to convay information to the user 39 | // Here we have an "animation" class and a "animationList" class. 40 | // The animation class contains information such as the name of the animation, the function it performs to actually update leds, and the names of variables 41 | // When creating a new animation, the constructor is set up so that it is automatically added to the animation linked list. This list is useful when the user wants to add a new animation through the serial monitor 42 | class animation; 43 | 44 | class animationList{ 45 | public: 46 | animation* start = NULL; 47 | int listSize = 0; 48 | 49 | } mainAnimationList; 50 | 51 | class animation{ 52 | public: 53 | String animationName; 54 | String variableNames; 55 | int numInputs; 56 | void (*playFunction)(int index); 57 | animation* next; 58 | 59 | animation(String newAnimationName, void (*functionPtr)(int), String newVariableNames, int NumInputs){ 60 | animationName = newAnimationName; 61 | variableNames = newVariableNames; 62 | playFunction = functionPtr; 63 | animation* current = mainAnimationList.start; 64 | numInputs = NumInputs; 65 | 66 | if(mainAnimationList.listSize == 0){ 67 | mainAnimationList.start = this; 68 | } 69 | else if(mainAnimationList.listSize == 1){ 70 | current->next = this; 71 | } 72 | else{ 73 | for(int i = 0; i < mainAnimationList.listSize - 1; i++){ 74 | current = current->next; 75 | } 76 | current->next = this; 77 | } 78 | mainAnimationList.listSize += 1; 79 | } 80 | }; 81 | 82 | 83 | 84 | 85 | //Function Pointer Array containing each instance of an animation to Cycle Through 86 | animation *animationArray[MAX_ANIMATIONS]; 87 | 88 | //Array of variables used by each 89 | int animationVariables[MAX_ANIMATIONS][VARIABLES_PER_ANIMATION]; 90 | 91 | void setup() { 92 | //Startup Delay 93 | delay(3000); 94 | 95 | //Begin the serial moniter. This sketch will use the serial moniter to interact with our controller 96 | Serial.begin(9600); 97 | 98 | //Setup strips and brightness 99 | FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalLEDStrip); 100 | FastLED.setBrightness(BRIGHTNESS); 101 | 102 | //Initialize our animation array 103 | clearAnimations(); 104 | Serial.println("Type something and press enter to edit controller"); 105 | } 106 | 107 | void loop() { 108 | 109 | //Check the serial monitor to see if 110 | serialUpdates(); 111 | 112 | //This gives all animations a trail like effect 113 | fadeToBlackBy(leds, NUM_LEDS, 20); 114 | 115 | //uncomment this if you don't want a fade effect 116 | //FastLED.clear(); 117 | 118 | //Run through our array of function pointers to play each animations 119 | playAnimations(); 120 | 121 | //Show updates on the strip 122 | FastLED.show(); 123 | FastLED.delay(1000/FRAMES_PER_SECOND); 124 | } 125 | 126 | 127 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ USER INTERFACE FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 128 | //Adds an animation through user input in the serial monitor 129 | void addAnimationSerial(){ 130 | int index = -1; 131 | int incoming = 0; 132 | int selectionNumber = 0; 133 | animation* current = mainAnimationList.start; 134 | 135 | //Attempt to find a free slot in our animation array 136 | index = findFreeAnimationSpot(); 137 | //If no spots available inform user and leave 138 | if(index == -1){ 139 | Serial.println("Animation Array is full"); 140 | return; 141 | } 142 | 143 | //Have the user select an animation to add 144 | Serial.println("Select an animation:"); 145 | for(int i = 0; i < mainAnimationList.listSize; i++){ 146 | Serial.print(i); 147 | Serial.print(": "); 148 | Serial.println(current->animationName); 149 | current = current->next; 150 | } 151 | while(1){ 152 | if(Serial.available() > 0){ 153 | incoming = Serial.read(); 154 | if(incoming == 10){ 155 | break; 156 | } 157 | selectionNumber = selectionNumber*10 + (incoming - 48); 158 | } 159 | } 160 | 161 | if(selectionNumber >= mainAnimationList.listSize){ 162 | Serial.println("Invalid Selection"); 163 | return; 164 | } 165 | 166 | current = mainAnimationList.start; 167 | for(int i = 0; i < selectionNumber; i++){ 168 | current = current->next; 169 | } 170 | animationArray[index] = current; 171 | Serial.print("You have selected: "); 172 | Serial.println(current->animationName); 173 | //Clear serial input incase user accidently entered something 174 | while(Serial.available()){ 175 | Serial.read(); 176 | } 177 | 178 | //Have the user fill in the variables 179 | Serial.println("Enter Variables"); 180 | Serial.println(current->variableNames); 181 | for(int i = 0; i < current->numInputs; i++){ 182 | Serial.print("Enter variable number: "); 183 | Serial.println(i); 184 | selectionNumber = 0; 185 | while(1){ 186 | if(Serial.available() > 0){ 187 | incoming = Serial.read(); 188 | if(incoming == 10){ 189 | break; 190 | } 191 | selectionNumber = selectionNumber*10 + (incoming - 48); 192 | } 193 | } 194 | Serial.print("Received: "); 195 | Serial.println(selectionNumber); 196 | animationVariables[index][i] = selectionNumber; 197 | } 198 | 199 | } 200 | 201 | void printDebugInfo(){ 202 | Serial.println("~~~~~~~~~~~~~~~~~~~DEBUG INFO~~~~~~~~~~~~~~~~~~~"); 203 | for(int i = 0; i < MAX_ANIMATIONS; i++){ 204 | Serial.print("Index: "); 205 | Serial.print(i); 206 | if(animationArray[i] == NULL){ 207 | Serial.println(" EMPTY"); 208 | continue; 209 | } 210 | Serial.print(" Name: "); 211 | Serial.print(animationArray[i]->animationName); 212 | for(int j = 0; j < animationArray[i]->numInputs; j++){ 213 | Serial.print(" V"); 214 | Serial.print(j); 215 | Serial.print(": "); 216 | Serial.print(animationVariables[i][j]); 217 | Serial.print(" "); 218 | } 219 | Serial.println(""); 220 | } 221 | } 222 | 223 | //Deletes an animation through user input in the seerial monitor 224 | void deleteAnimationSerial(){ 225 | int incoming = 0; 226 | int selectionNumber = 0; 227 | Serial.println("Enter the index of the animation you wish to delete"); 228 | printDebugInfo(); 229 | while(1){ 230 | if(Serial.available() > 0){ 231 | incoming = Serial.read(); 232 | if(incoming == 10){ 233 | break; 234 | } 235 | selectionNumber = selectionNumber*10 + (incoming - 48); 236 | } 237 | } 238 | if(selectionNumber < 0 || selectionNumber >= MAX_ANIMATIONS){ 239 | Serial.println("Invalid Selection"); 240 | return; 241 | } 242 | deleteAnimation(selectionNumber); 243 | } 244 | 245 | void serialUpdates(){ 246 | int incoming; 247 | //Check if user has made input 248 | if(Serial.available() > 0){ 249 | //Clear serial input incase user entered multiple characters 250 | while(Serial.available() > 0){ 251 | Serial.read(); 252 | } 253 | 254 | //Print Options 255 | Serial.println("Select an Option"); 256 | Serial.println("1: Add an animation"); 257 | Serial.println("2: Delete an animation"); 258 | Serial.println("3: Clear all animations"); 259 | Serial.println("4: Print Debug Info"); 260 | 261 | while(1){ 262 | if(Serial.available() > 0){ 263 | incoming = Serial.read(); 264 | break; 265 | } 266 | } 267 | 268 | //Clear the rest incase user entered multiple characters; 269 | while(Serial.available() > 0){ 270 | Serial.read(); 271 | } 272 | //Perform user entered action 273 | switch(incoming){ 274 | case '1': 275 | addAnimationSerial(); 276 | break; 277 | 278 | case '2': 279 | deleteAnimationSerial(); 280 | break; 281 | 282 | case '3': 283 | clearAnimations(); 284 | break; 285 | 286 | case '4': 287 | printDebugInfo(); 288 | break; 289 | 290 | default: 291 | Serial.println("Invalid Selection"); 292 | } 293 | Serial.println("Type something and press enter to edit controller"); 294 | } 295 | 296 | } 297 | 298 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ACTUAL ENGINE FUNCTIONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 299 | 300 | //This function will run through our array of function pointers and play all animations which have been loaded. 301 | //The variable "indexNumber" is passed as a parameter so that each instance of an animation knows where to find it's variables 302 | void playAnimations(){ 303 | for(int indexNumber = 0; indexNumber < MAX_ANIMATIONS; indexNumber++){ 304 | if(animationArray[indexNumber] != NULL){ 305 | animationArray[indexNumber]->playFunction(indexNumber); 306 | } 307 | } 308 | } 309 | 310 | // "Remove" an animation from our animation array 311 | void deleteAnimation(int index){ 312 | animationArray[index] = NULL; 313 | } 314 | 315 | 316 | //Sets all function pointers to NULL, efectively clearing our controller 317 | void clearAnimations(){ 318 | for(int i = 0; i < MAX_ANIMATIONS; i++){ 319 | animationArray[i] = NULL; 320 | } 321 | } 322 | 323 | //Goes through our animation array and attempts to find a free location. This function is called by animations that will create other animations 324 | int findFreeAnimationSpot(){ 325 | for(int i = 0; i < MAX_ANIMATIONS; i++){ 326 | if(animationArray[i] == NULL){ 327 | return i; 328 | } 329 | } 330 | //No slot found 331 | return -1; 332 | } 333 | 334 | 335 | 336 | //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ANIMATION FUNCTIONS~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 337 | //Below is all of the animations. To add your own follow the format of the example functions and then create a new animation object. The parameters for the constructor are: animation name, corresponding function, variable names, number of variables 338 | //To ensure everything works, all animations should be written in a non blocking way 339 | 340 | // A recreation of the sinelon animation from demo reel 100 made to fit this engine 341 | void sinelon(int index){ 342 | int start = animationVariables[index][0]; 343 | int end = animationVariables[index][1]; 344 | int bpm = animationVariables[index][2]; 345 | int hue = animationVariables[index][3]; 346 | int phase = animationVariables[index][4]; 347 | 348 | int pos = beatsin16(bpm, start, end, phase); 349 | leds[pos] += CHSV(hue, 255, 255); 350 | } 351 | animation Sinelon("Sinelon", sinelon, "Start, End, BPM, HUE, Phase", 5); 352 | 353 | // Fills a solid section of the strip in 354 | void solidBlock(int index){ 355 | int start = animationVariables[index][0]; 356 | int end = animationVariables[index][1]; 357 | int hue = animationVariables[index][2]; 358 | 359 | for(int i = start; i <= end; i++){ 360 | leds[i] += CHSV(hue, 255, 255); 361 | } 362 | } 363 | animation SolidBlock("Solid Block", solidBlock, "Start, End, Hue", 3); 364 | 365 | // Pulses a block in and out 366 | void pulsingBlock(int index){ 367 | int start = animationVariables[index][0]; 368 | int end = animationVariables[index][1]; 369 | int bpm = animationVariables[index][2]; 370 | int hue = animationVariables[index][3]; 371 | int phase = animationVariables[index][4]; 372 | 373 | int bright = beatsin16(bpm, 0, 255, phase); 374 | for(int i = start; i <= end; i++){ 375 | leds[i] += CHSV(hue, 255, bright); 376 | } 377 | 378 | } 379 | animation PulsingBlock("Pulsing Block", pulsingBlock, "Start, End, BPM, HUE, Phase", 5); 380 | 381 | //Sometimes you want to update a variable within your animation. This animation shows an example of how that can be done using pointers 382 | void movingDot(int index){ 383 | int start = animationVariables[index][0]; 384 | int end = animationVariables[index][1]; 385 | int hue = animationVariables[index][2]; 386 | int *pos = &animationVariables[index][3]; //We use a pointer here so we can update the value of this variable everytime this function is called. 387 | 388 | leds[*pos] += CHSV(hue, 255, 255); //Make sure to access the content of the pointer and not the pointer itself 389 | *pos = start + (start + *pos + 1)%(end + 1); //Position will have a new value next time this function is called 390 | } 391 | animation MovingDot("Moving Dot", movingDot, "Start, End, HUE, Starting Position", 4); 392 | 393 | //Sometimes you want temporary animations that can be created by other animations. This temporary animation fades away and then deletes iteself once it is gone 394 | void fadingBlock(int index){ 395 | int start = animationVariables[index][0]; 396 | int end = animationVariables[index][1]; 397 | int hue = animationVariables[index][2]; 398 | int *brightness = &animationVariables[index][3]; //Using another pointer here to keep track of the brightness. When this value is 0 we will delete this 399 | int fadeRate = animationVariables[index][4]; 400 | 401 | if (fadeRate > 255){ 402 | fadeRate = 255; 403 | }else if(fadeRate < 1){ 404 | fadeRate = 1; 405 | } 406 | 407 | *brightness = (*brightness * (255 - fadeRate))/255; 408 | 409 | if (*brightness <= 0){ 410 | deleteAnimation(index); 411 | return; 412 | } 413 | 414 | for(int i = start; i <= end; i++){ 415 | leds[i] += CHSV(hue, 255, *brightness); 416 | } 417 | } 418 | animation FadingBlock("Temporary Fading Block", fadingBlock, "Start, End, hue, Initial Brightness (0 - 255), fadeRate (0 - 255)", 5); 419 | 420 | 421 | void fadingBlockGenerator(int index){ 422 | int start = animationVariables[index][0]; 423 | int end = animationVariables[index][1]; 424 | int period = animationVariables[index][2]; 425 | int *prevTime = &animationVariables[index][3]; 426 | 427 | if(millis() - (unsigned long long int) *prevTime > period){ 428 | int index = findFreeAnimationSpot(); 429 | if(index == -1){ 430 | return; 431 | } 432 | animationArray[index] = &FadingBlock; 433 | int pos = random(start, end - 5); 434 | animationVariables[index][0] = pos; 435 | animationVariables[index][1] = pos + random(0, 5); 436 | animationVariables[index][2] = random8(); 437 | animationVariables[index][3] = 255; 438 | animationVariables[index][4] = random(1, 50); 439 | *prevTime = millis(); 440 | } 441 | } 442 | animation FadingBlockGenerator("Random Fading Block Generator", fadingBlockGenerator, "Start, End, Period(in milliseconds), Internal Variable (Enter 0)", 4); 443 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution-NonCommercial-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 58 | Public License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-NonCommercial-ShareAlike 4.0 International Public License 63 | ("Public License"). To the extent this Public License may be 64 | interpreted as a contract, You are granted the Licensed Rights in 65 | consideration of Your acceptance of these terms and conditions, and the 66 | Licensor grants You such rights in consideration of benefits the 67 | Licensor receives from making the Licensed Material available under 68 | these terms and conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-NC-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution, NonCommercial, and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. NonCommercial means not primarily intended for or directed towards 126 | commercial advantage or monetary compensation. For purposes of 127 | this Public License, the exchange of the Licensed Material for 128 | other material subject to Copyright and Similar Rights by digital 129 | file-sharing or similar means is NonCommercial provided there is 130 | no payment of monetary compensation in connection with the 131 | exchange. 132 | 133 | l. Share means to provide material to the public by any means or 134 | process that requires permission under the Licensed Rights, such 135 | as reproduction, public display, public performance, distribution, 136 | dissemination, communication, or importation, and to make material 137 | available to the public including in ways that members of the 138 | public may access the material from a place and at a time 139 | individually chosen by them. 140 | 141 | m. Sui Generis Database Rights means rights other than copyright 142 | resulting from Directive 96/9/EC of the European Parliament and of 143 | the Council of 11 March 1996 on the legal protection of databases, 144 | as amended and/or succeeded, as well as other essentially 145 | equivalent rights anywhere in the world. 146 | 147 | n. You means the individual or entity exercising the Licensed Rights 148 | under this Public License. Your has a corresponding meaning. 149 | 150 | 151 | Section 2 -- Scope. 152 | 153 | a. License grant. 154 | 155 | 1. Subject to the terms and conditions of this Public License, 156 | the Licensor hereby grants You a worldwide, royalty-free, 157 | non-sublicensable, non-exclusive, irrevocable license to 158 | exercise the Licensed Rights in the Licensed Material to: 159 | 160 | a. reproduce and Share the Licensed Material, in whole or 161 | in part, for NonCommercial purposes only; and 162 | 163 | b. produce, reproduce, and Share Adapted Material for 164 | NonCommercial purposes only. 165 | 166 | 2. Exceptions and Limitations. For the avoidance of doubt, where 167 | Exceptions and Limitations apply to Your use, this Public 168 | License does not apply, and You do not need to comply with 169 | its terms and conditions. 170 | 171 | 3. Term. The term of this Public License is specified in Section 172 | 6(a). 173 | 174 | 4. Media and formats; technical modifications allowed. The 175 | Licensor authorizes You to exercise the Licensed Rights in 176 | all media and formats whether now known or hereafter created, 177 | and to make technical modifications necessary to do so. The 178 | Licensor waives and/or agrees not to assert any right or 179 | authority to forbid You from making technical modifications 180 | necessary to exercise the Licensed Rights, including 181 | technical modifications necessary to circumvent Effective 182 | Technological Measures. For purposes of this Public License, 183 | simply making modifications authorized by this Section 2(a) 184 | (4) never produces Adapted Material. 185 | 186 | 5. Downstream recipients. 187 | 188 | a. Offer from the Licensor -- Licensed Material. Every 189 | recipient of the Licensed Material automatically 190 | receives an offer from the Licensor to exercise the 191 | Licensed Rights under the terms and conditions of this 192 | Public License. 193 | 194 | b. Additional offer from the Licensor -- Adapted Material. 195 | Every recipient of Adapted Material from You 196 | automatically receives an offer from the Licensor to 197 | exercise the Licensed Rights in the Adapted Material 198 | under the conditions of the Adapter's License You apply. 199 | 200 | c. No downstream restrictions. You may not offer or impose 201 | any additional or different terms or conditions on, or 202 | apply any Effective Technological Measures to, the 203 | Licensed Material if doing so restricts exercise of the 204 | Licensed Rights by any recipient of the Licensed 205 | Material. 206 | 207 | 6. No endorsement. Nothing in this Public License constitutes or 208 | may be construed as permission to assert or imply that You 209 | are, or that Your use of the Licensed Material is, connected 210 | with, or sponsored, endorsed, or granted official status by, 211 | the Licensor or others designated to receive attribution as 212 | provided in Section 3(a)(1)(A)(i). 213 | 214 | b. Other rights. 215 | 216 | 1. Moral rights, such as the right of integrity, are not 217 | licensed under this Public License, nor are publicity, 218 | privacy, and/or other similar personality rights; however, to 219 | the extent possible, the Licensor waives and/or agrees not to 220 | assert any such rights held by the Licensor to the limited 221 | extent necessary to allow You to exercise the Licensed 222 | Rights, but not otherwise. 223 | 224 | 2. Patent and trademark rights are not licensed under this 225 | Public License. 226 | 227 | 3. To the extent possible, the Licensor waives any right to 228 | collect royalties from You for the exercise of the Licensed 229 | Rights, whether directly or through a collecting society 230 | under any voluntary or waivable statutory or compulsory 231 | licensing scheme. In all other cases the Licensor expressly 232 | reserves any right to collect such royalties, including when 233 | the Licensed Material is used other than for NonCommercial 234 | purposes. 235 | 236 | 237 | Section 3 -- License Conditions. 238 | 239 | Your exercise of the Licensed Rights is expressly made subject to the 240 | following conditions. 241 | 242 | a. Attribution. 243 | 244 | 1. If You Share the Licensed Material (including in modified 245 | form), You must: 246 | 247 | a. retain the following if it is supplied by the Licensor 248 | with the Licensed Material: 249 | 250 | i. identification of the creator(s) of the Licensed 251 | Material and any others designated to receive 252 | attribution, in any reasonable manner requested by 253 | the Licensor (including by pseudonym if 254 | designated); 255 | 256 | ii. a copyright notice; 257 | 258 | iii. a notice that refers to this Public License; 259 | 260 | iv. a notice that refers to the disclaimer of 261 | warranties; 262 | 263 | v. a URI or hyperlink to the Licensed Material to the 264 | extent reasonably practicable; 265 | 266 | b. indicate if You modified the Licensed Material and 267 | retain an indication of any previous modifications; and 268 | 269 | c. indicate the Licensed Material is licensed under this 270 | Public License, and include the text of, or the URI or 271 | hyperlink to, this Public License. 272 | 273 | 2. You may satisfy the conditions in Section 3(a)(1) in any 274 | reasonable manner based on the medium, means, and context in 275 | which You Share the Licensed Material. For example, it may be 276 | reasonable to satisfy the conditions by providing a URI or 277 | hyperlink to a resource that includes the required 278 | information. 279 | 3. If requested by the Licensor, You must remove any of the 280 | information required by Section 3(a)(1)(A) to the extent 281 | reasonably practicable. 282 | 283 | b. ShareAlike. 284 | 285 | In addition to the conditions in Section 3(a), if You Share 286 | Adapted Material You produce, the following conditions also apply. 287 | 288 | 1. The Adapter's License You apply must be a Creative Commons 289 | license with the same License Elements, this version or 290 | later, or a BY-NC-SA Compatible License. 291 | 292 | 2. You must include the text of, or the URI or hyperlink to, the 293 | Adapter's License You apply. You may satisfy this condition 294 | in any reasonable manner based on the medium, means, and 295 | context in which You Share Adapted Material. 296 | 297 | 3. You may not offer or impose any additional or different terms 298 | or conditions on, or apply any Effective Technological 299 | Measures to, Adapted Material that restrict exercise of the 300 | rights granted under the Adapter's License You apply. 301 | 302 | 303 | Section 4 -- Sui Generis Database Rights. 304 | 305 | Where the Licensed Rights include Sui Generis Database Rights that 306 | apply to Your use of the Licensed Material: 307 | 308 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 309 | to extract, reuse, reproduce, and Share all or a substantial 310 | portion of the contents of the database for NonCommercial purposes 311 | only; 312 | 313 | b. if You include all or a substantial portion of the database 314 | contents in a database in which You have Sui Generis Database 315 | Rights, then the database in which You have Sui Generis Database 316 | Rights (but not its individual contents) is Adapted Material, 317 | including for purposes of Section 3(b); and 318 | 319 | c. You must comply with the conditions in Section 3(a) if You Share 320 | all or a substantial portion of the contents of the database. 321 | 322 | For the avoidance of doubt, this Section 4 supplements and does not 323 | replace Your obligations under this Public License where the Licensed 324 | Rights include other Copyright and Similar Rights. 325 | 326 | 327 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 328 | 329 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 330 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 331 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 332 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 333 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 334 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 335 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 336 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 337 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 338 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 339 | 340 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 341 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 342 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 343 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 344 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 345 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 346 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 347 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 348 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 349 | 350 | c. The disclaimer of warranties and limitation of liability provided 351 | above shall be interpreted in a manner that, to the extent 352 | possible, most closely approximates an absolute disclaimer and 353 | waiver of all liability. 354 | 355 | 356 | Section 6 -- Term and Termination. 357 | 358 | a. This Public License applies for the term of the Copyright and 359 | Similar Rights licensed here. However, if You fail to comply with 360 | this Public License, then Your rights under this Public License 361 | terminate automatically. 362 | 363 | b. Where Your right to use the Licensed Material has terminated under 364 | Section 6(a), it reinstates: 365 | 366 | 1. automatically as of the date the violation is cured, provided 367 | it is cured within 30 days of Your discovery of the 368 | violation; or 369 | 370 | 2. upon express reinstatement by the Licensor. 371 | 372 | For the avoidance of doubt, this Section 6(b) does not affect any 373 | right the Licensor may have to seek remedies for Your violations 374 | of this Public License. 375 | 376 | c. For the avoidance of doubt, the Licensor may also offer the 377 | Licensed Material under separate terms or conditions or stop 378 | distributing the Licensed Material at any time; however, doing so 379 | will not terminate this Public License. 380 | 381 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 382 | License. 383 | 384 | 385 | Section 7 -- Other Terms and Conditions. 386 | 387 | a. The Licensor shall not be bound by any additional or different 388 | terms or conditions communicated by You unless expressly agreed. 389 | 390 | b. Any arrangements, understandings, or agreements regarding the 391 | Licensed Material not stated herein are separate from and 392 | independent of the terms and conditions of this Public License. 393 | 394 | 395 | Section 8 -- Interpretation. 396 | 397 | a. For the avoidance of doubt, this Public License does not, and 398 | shall not be interpreted to, reduce, limit, restrict, or impose 399 | conditions on any use of the Licensed Material that could lawfully 400 | be made without permission under this Public License. 401 | 402 | b. To the extent possible, if any provision of this Public License is 403 | deemed unenforceable, it shall be automatically reformed to the 404 | minimum extent necessary to make it enforceable. If the provision 405 | cannot be reformed, it shall be severed from this Public License 406 | without affecting the enforceability of the remaining terms and 407 | conditions. 408 | 409 | c. No term or condition of this Public License will be waived and no 410 | failure to comply consented to unless expressly agreed to by the 411 | Licensor. 412 | 413 | d. Nothing in this Public License constitutes or may be interpreted 414 | as a limitation upon, or waiver of, any privileges and immunities 415 | that apply to the Licensor or You, including from the legal 416 | processes of any jurisdiction or authority. 417 | 418 | ======================================================================= 419 | 420 | Creative Commons is not a party to its public 421 | licenses. Notwithstanding, Creative Commons may elect to apply one of 422 | its public licenses to material it publishes and in those instances 423 | will be considered the “Licensor.” The text of the Creative Commons 424 | public licenses is dedicated to the public domain under the CC0 Public 425 | Domain Dedication. Except for the limited purpose of indicating that 426 | material is shared under a Creative Commons public license or as 427 | otherwise permitted by the Creative Commons policies published at 428 | creativecommons.org/policies, Creative Commons does not authorize the 429 | use of the trademark "Creative Commons" or any other trademark or logo 430 | of Creative Commons without its prior written consent including, 431 | without limitation, in connection with any unauthorized modifications 432 | to any of its public licenses or any other arrangements, 433 | understandings, or agreements concerning use of licensed material. For 434 | the avoidance of doubt, this paragraph does not form part of the 435 | public licenses. 436 | 437 | Creative Commons may be contacted at creativecommons.org. 438 | --------------------------------------------------------------------------------