diff --git a/Software/editor-modes.cpp b/Software/editor-modes.cpp new file mode 100644 index 0000000..1d6d0cd --- /dev/null +++ b/Software/editor-modes.cpp @@ -0,0 +1,694 @@ +#include + +#include "editor-modes.h" +#include "editor.h" +#include "panel-scanner.h" +#include "player.h" +#include "pattern.h" + +extern PanelScanner theScanner; +extern Player thePlayer; +extern Pattern thePattern; +extern Editor theEditor; + +// Local definitions + + +static const int32_t PATTERN_SEL_INDICATOR = 0x10; +static const int32_t PATTERN_CHAIN_INDICATOR = 0x11; +static const int32_t STEP_EDIT_INDICATOR = 0x12; +static const int32_t STEP_ACCENT_INDICATOR = 0x13; +static const int32_t VOICE_SEL_INDICATOR = 0x14; +static const int32_t MUTE_SEL_INDICATOR = 0x15; +static const int32_t UTILITY_SEL_INDICATOR = 0x16; +static const int32_t PLAY_INDICATOR = 0x17; + /* +static const int32_t PATTERN_SEL_INDICATOR = 0x17; +static const int32_t PATTERN_CHAIN_INDICATOR = 0x16; +static const int32_t STEP_EDIT_INDICATOR = 0x15; +static const int32_t STEP_ACCENT_INDICATOR = 0x14; +static const int32_t VOICE_SEL_INDICATOR = 0x13; +static const int32_t MUTE_SEL_INDICATOR = 0x12; +static const int32_t UTILITY_SEL_INDICATOR = 0x11; +static const int32_t PLAY_INDICATOR = 0x10; +*/ + + + +/////////////////////////////////////////////////////////////////////////////////////// +// common implementation because chain display +// is used by chain and patt sel modes. +/////////////////////////////////////////////////////////////////////////////////////// + +static void doChainLeds() +{ + if(thePlayer.chainIsActive()) + { + theScanner.clearAllBlinkingLEDs(); + + for(uint32_t i = 0; i < Pattern::NUM_PATTERNS; i++) + { + theScanner.setBlinkingLED(i, thePlayer.checkChainMembership(i)); + } + // blinking overrides background, so + // to mark current pattern, unset blinking, + // set background + theScanner.clearBlinkingLED(thePattern.getCurrentPattern()); + theScanner.setBackgroundLED(thePattern.getCurrentPattern()); + } +} + +static void doPlayingLed() +{ + theScanner.clearAllOverlayLEDs(); + + if(thePlayer.isPlaying()) + { + theScanner.setOverlayLED(thePlayer.getCurrentStep()); + theScanner.setBackgroundLED(PLAY_INDICATOR); + } + else + { + theScanner.clearBackgroundLED(PLAY_INDICATOR); + } + + +} + +/////////////////////////////////////////////////////////////////////////////////////// +//////// Base Class +/////////////////////////////////////////////////////////////////////////////////////// + +// constructor... +pvEditorMode::pvEditorMode() +{ +} + +// Common handling for the start/stop key +void pvEditorMode::HandlePlayButton(bool pressed) +{ + Serial.write("Play/Pause"); + if(pressed ) + { + if(thePlayer.isPlaying()) + { + thePlayer.stop(); + } + else + { + thePlayer.start(); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////////////// +//////// Step Editor +/////////////////////////////////////////////////////////////////////////////////////// + +StepEdit::StepEdit(): pvEditorMode() +{ + //current_voice = 0; + +} + +void StepEdit::HandleKey(uint32_t keynum, bool pressed) +{ + bool setting; + + if (keynum == PLAY_INDICATOR) //start/stop key + { + HandlePlayButton(pressed); + } + else if((keynum >= 0) && (keynum <= 15)) + { + if(pressed) + { + setting = thePattern.toggleBit(keynum); + + // If we're clearing a bit, lose it's accent, too. + if(!setting) + { + if(thePattern.getAccentBit(keynum)) + thePattern.toggleAccentBit(keynum); + } + + setLEDs(true); + } + } + else if(keynum == VOICE_SEL_INDICATOR) // voice select mode + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_VOICE_SEL); + } + } + else if(keynum == STEP_ACCENT_INDICATOR) // voice select mode + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_STEP_ACCENT); + } + } + + else if(keynum == MUTE_SEL_INDICATOR) + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_MUTE_SEL); + } + } + else if(keynum == PATTERN_SEL_INDICATOR) // pattern select mode + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_PATT_SEL); + } + } + else if(keynum == UTILITY_SEL_INDICATOR) + { + if(pressed)// && (!thePlayer.isPlaying())) + { + theEditor.setMode(Editor::eMODE_UTILITY); + } + } + +#if 0 + else// other, unmapped keys, just show they work. + { + theScanner.setBackgroundLED(keynum, pressed); + } +#endif + +} + +void StepEdit::setLEDs(bool entry) +{ + // Initialize LEDs + if(entry) + { + doPlayingLed(); + + theScanner.setBackgroundLED(STEP_EDIT_INDICATOR); + + for(uint32_t i = 0; i < Pattern::PATTERN_LEN; i++) + { + theScanner.setBackgroundLED(i, thePattern.getVoiceBit(i)); + } + } + else // on exit + { + theScanner.clearAllHalfLEDs(); + theScanner.clearAllBackgroundLEDs(); + theScanner.clearAllBlinkingLEDs(); + } + +} + +/////////////////////////////////////////////////////////////////////////////////////// +//////// Accent Editor +/////////////////////////////////////////////////////////////////////////////////////// + +StepAccent::StepAccent(): pvEditorMode() +{ + //current_voice = 0; + +} + +void StepAccent::HandleKey(uint32_t keynum, bool pressed) +{ + bool setting; + Serial.write("Step Accent"); + + if(keynum == STEP_ACCENT_INDICATOR) + { + if(!pressed) + { + theEditor.setMode(Editor::eMODE_STEP_EDIT); + } + } + else if (keynum == PLAY_INDICATOR) //start/stop key + { + HandlePlayButton(pressed); + } + else if((keynum >= 0) && (keynum <= 15)) + { + if(pressed) + { + setting = thePattern.toggleAccentBit(keynum); + + // If we just added an accent, but note isn't triggered, trigger it. + // Likewise, if we cleared and accent, clear the trigger. + // Don't leave stranded accents, because they're an ugly surprise later... + if( (setting && !thePattern.getVoiceBit(keynum) ) || + (!setting && thePattern.getVoiceBit(keynum))) + { + thePattern.toggleBit(keynum); + } + + setLEDs(true); + } + } +} + +void StepAccent::setLEDs(bool entry) +{ + // Initialize LEDs + if(entry) + { + doPlayingLed(); + + theScanner.setBackgroundLED(STEP_ACCENT_INDICATOR); + + for(uint32_t i = 0; i < Pattern::PATTERN_LEN; i++) + { + theScanner.setBackgroundLED(i, thePattern.getAccentBit(i)); + theScanner.setHalfLED(i, thePattern.getVoiceBit(i)); + } + } + else // on exit + { + theScanner.clearAllHalfLEDs(); + theScanner.clearAllBackgroundLEDs(); + theScanner.clearAllBlinkingLEDs(); + } + +} + + +/////////////////////////////////////////////////////////////////////////////////////// +//////// Voice Selector +/////////////////////////////////////////////////////////////////////////////////////// + +VoiceSelect::VoiceSelect(): pvEditorMode() +{ + +} + +void VoiceSelect::HandleKey(uint32_t keynum, bool pressed) +{ + Serial.write("Voice select"); + + if(keynum == VOICE_SEL_INDICATOR) // voice select mode + { + if(!pressed) + { + theEditor.setMode(Editor::eMODE_STEP_EDIT); + } + } + else if (keynum == PLAY_INDICATOR) //start/stop key + { + HandlePlayButton(pressed); + } + else if((keynum >= 0) && (keynum <= 10)) + { + // TBD - more voices means accept more input + if(pressed) + { + thePattern.setCurrentVoice(keynum); + setLEDs(true); + } + } +} + +void VoiceSelect::setLEDs(bool entry) +{ + uint32_t bitnum; + + if(entry) + { + theScanner.clearAllBackgroundLEDs(); + + doPlayingLed(); + + // Mode indication + theScanner.setBackgroundLED(VOICE_SEL_INDICATOR); + + // Present voice indication + bitnum = thePattern.getCurrentVoice(); + theScanner.setBackgroundLED(bitnum); + } + else + { + theScanner.clearAllBackgroundLEDs(); + } +} + + +/////////////////////////////////////////////////////////////////////////////////////// +//////// Mute Selector +/////////////////////////////////////////////////////////////////////////////////////// + +MuteSelect::MuteSelect(): pvEditorMode() +{ + +} + +void MuteSelect::HandleKey(uint32_t keynum, bool pressed) +{ +// bool setting; + Serial.write("Mute select"); + + if(keynum == STEP_EDIT_INDICATOR) // voice select mode + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_STEP_EDIT); + } + } + else if(keynum == PATTERN_SEL_INDICATOR) // pattern select mode + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_PATT_SEL); + } + } + else if (keynum == PLAY_INDICATOR) //start/stop key + { + HandlePlayButton(pressed); + } + else if(keynum == UTILITY_SEL_INDICATOR) + { + if(pressed)// && (!thePlayer.isPlaying())) + { + theEditor.setMode(Editor::eMODE_UTILITY); + } + } + + else if((keynum >= 0) && (keynum <= 10)) + { + if(pressed) + { + bool setting; + setting = thePlayer.toggleMuteBit(keynum); + setLEDs(true); + if (setting == true) {}; + + } + } +} + +void MuteSelect::setLEDs(bool entry) +{ + if(entry) + { + doPlayingLed(); + + // set mode indicator + theScanner.setBackgroundLED(MUTE_SEL_INDICATOR); + + // and display data on editor buttons + for(uint32_t i = 0; i < 12; i++) + { + theScanner.setBackgroundLED(i, thePlayer.getMuteBit(i)); + // blinking overrides background + theScanner.setBlinkingLED(i, thePlayer.getPendingMuteBit(i)); + } + + + } + else + { + theScanner.clearAllBackgroundLEDs(); + theScanner.clearAllBlinkingLEDs(); + } +} + +/////////////////////////////////////////////////////////////////////////////////////// +//////// Pattern Selector +/////////////////////////////////////////////////////////////////////////////////////// + + +PatternSelect::PatternSelect() +{ + +} + +void PatternSelect::HandleKey(uint32_t keynum, bool pressed) +{ + Serial.write("Pattern select"); + + if(keynum == STEP_EDIT_INDICATOR) + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_STEP_EDIT); + } + } + else if(keynum == MUTE_SEL_INDICATOR) + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_MUTE_SEL); + } + } + else if(keynum == UTILITY_SEL_INDICATOR) + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_UTILITY); + } + } + else if(keynum == PATTERN_CHAIN_INDICATOR) + { + if(pressed) + { + thePlayer.initChain(); + theEditor.setMode(Editor::eMODE_CHAIN_EDIT); + } + } + else if (keynum == PLAY_INDICATOR) //start/stop key + { + HandlePlayButton(pressed); + } + else if((keynum >= 0) && (keynum <= 15)) + { + if(pressed) + { + // if there was a chain, end it + if(thePlayer.chainIsActive()) + { + thePlayer.initChain(); + } + + thePlayer.setNextPattern(keynum); + setLEDs(true); + } + } +} + +void PatternSelect::setLEDs(bool entry) +{ + int32_t pending; + + if(entry) + { + theScanner.clearAllBackgroundLEDs(); + theScanner.clearAllBlinkingLEDs(); + + doPlayingLed(); + + // set mode indicator + theScanner.setBackgroundLED(PATTERN_SEL_INDICATOR); + + if(!thePlayer.chainIsActive()) + { + theScanner.setBackgroundLED(thePattern.getCurrentPattern()); + pending = thePlayer.getPendingPattern(); + if(pending != -1) + { + theScanner.setBlinkingLED(pending); + } + } + else + { + doChainLeds(); + } + } + else + { + theScanner.clearAllBackgroundLEDs(); + theScanner.clearAllBlinkingLEDs(); + } + +} + +/////////////////////////////////////////////////////////////////////////////////////// +//////// Chain Editor +/////////////////////////////////////////////////////////////////////////////////////// + + +ChainEdit::ChainEdit() +{ + +} + +void ChainEdit::HandleKey(uint32_t keynum, bool pressed) +{ + Serial.write("Chain editor"); + + if(keynum == PATTERN_CHAIN_INDICATOR) + { + if(!pressed) + { + theEditor.setMode(Editor::eMODE_PATT_SEL); + } + } + else if(keynum == PLAY_INDICATOR) //start/stop key + { + HandlePlayButton(pressed); + } + else if((keynum >= 0) && (keynum <= 15)) + { + if(pressed) + { + thePlayer.addToChain(keynum); + } + } +} + +void ChainEdit::setLEDs(bool entry) +{ + if(entry) + { + doPlayingLed(); + + doChainLeds(); + + // set mode indicator + theScanner.setBackgroundLED(PATTERN_CHAIN_INDICATOR); + } + else + { + theScanner.clearAllBackgroundLEDs(); + theScanner.clearAllBlinkingLEDs(); + } + +} + + +/////////////////////////////////////////////////////////////////////////////////////// +//////// Utility mode +/////////////////////////////////////////////////////////////////////////////////////// + +UtilityMode::UtilityMode(): pvEditorMode() +{ + +} + +void UtilityMode::HandleKey(uint32_t keynum, bool pressed) +{ + Serial.write("Utility mode"); + + if(keynum == STEP_EDIT_INDICATOR) // voice select mode + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_STEP_EDIT); + } + } + else if(keynum == MUTE_SEL_INDICATOR) + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_MUTE_SEL); + } + } + else if(keynum == PATTERN_SEL_INDICATOR) // pattern select mode + { + if(pressed) + { + theEditor.setMode(Editor::eMODE_PATT_SEL); + } + } + else if (keynum == PLAY_INDICATOR) //start/stop key + { + HandlePlayButton(pressed); + } + else if((keynum >= 0) && (keynum <= 15)) + { + doUtilMode(keynum, pressed); + + } +} + +void UtilityMode::setLEDs(bool entry) +{ + //uint32_t bitnum; + + if(entry) + { + doPlayingLed(); + + // Mode indication + theScanner.setBackgroundLED(UTILITY_SEL_INDICATOR); + + // Present data indication + theScanner.setBackgroundLED(0x2, thePlayer.getSwing()); + } + else + { + theScanner.clearAllBackgroundLEDs(); + } +} + +void UtilityMode::doUtilMode(uint32_t keynum, bool pressed) +{ + bool playing = thePlayer.isPlaying(); + + if(!pressed) + { + return; + } + + switch(keynum) + { + case 0: + { + if(!playing) + { + thePattern.clearCurrentPattern(); + } + } + break; + case 1: + { + if(!playing) + { + thePattern.randomizeCurrentPattern(); + } + } + break; + case 2: + { + thePlayer.toggleSwing(); + setLEDs(true); + } + break; + case 14: + { + if(!playing) + { + delay(100); + + thePattern.writeToCard(); + + delay(100); + } + } + break; + case 15: + { + if(!playing) + { + delay(100); + + thePattern.readFromCard(); + + delay(100); + } + } + break; + + } +} diff --git a/Software/editor-modes.h b/Software/editor-modes.h new file mode 100644 index 0000000..c914c63 --- /dev/null +++ b/Software/editor-modes.h @@ -0,0 +1,104 @@ +#ifndef _EDITOR_MODES_H_ +#define _EDITOR_MODES_H_ + +#pragma once + +#include + +class pvEditorMode +{ + public: + + pvEditorMode(); + + virtual void HandleKey(uint32_t keynum, bool pressed) = 0; + virtual void setLEDs(bool entry) = 0; + + void HandlePlayButton(bool pressed); + + private: +}; + + +class StepEdit: public pvEditorMode +{ + public: + + StepEdit(); + virtual void HandleKey(uint32_t keynum, bool pressed); + virtual void setLEDs(bool entry); + private: + +}; + +class StepAccent: public pvEditorMode +{ + public: + + StepAccent(); + virtual void HandleKey(uint32_t keynum, bool pressed); + virtual void setLEDs(bool entry); + private: + +}; + +class VoiceSelect: public pvEditorMode +{ + public: + + VoiceSelect(); + virtual void HandleKey(uint32_t keynum, bool pressed); + virtual void setLEDs(bool entry); + + private: +}; + +class MuteSelect: public pvEditorMode +{ + public: + + MuteSelect(); + virtual void HandleKey(uint32_t keynum, bool pressed); + virtual void setLEDs(bool entry); + + private: +}; + +class PatternSelect: public pvEditorMode +{ + public: + + PatternSelect(); + virtual void HandleKey(uint32_t keynum, bool pressed); + virtual void setLEDs(bool entry); + + private: +}; + +class ChainEdit: public pvEditorMode +{ + public: + + ChainEdit(); + virtual void HandleKey(uint32_t keynum, bool pressed); + virtual void setLEDs(bool entry); + + private: +}; + + + +class UtilityMode: public pvEditorMode +{ + public: + + UtilityMode(); + virtual void HandleKey(uint32_t keynum, bool pressed); + virtual void setLEDs(bool entry); + + private: + + void doUtilMode(uint32_t keynum, bool pressed); +}; + +#endif // keepout diff --git a/Software/editor.cpp b/Software/editor.cpp new file mode 100644 index 0000000..425d535 --- /dev/null +++ b/Software/editor.cpp @@ -0,0 +1,102 @@ +#include + +#include "editor.h" +#include "player.h" + +extern PanelScanner theScanner; +extern Player thePlayer; + + +// decls for the mode objects +static StepEdit stepEditor; +static StepAccent accentEditor; +static ChainEdit chainEditor; +static VoiceSelect voiceSelector; +static MuteSelect muteSelector; +static PatternSelect patternSelector; +static UtilityMode utilityMode; + +// constructor... +Editor::Editor() +{ + // don't refer to other classes here, they may not yet be constructed/init'd + // IE: the pattern may not be constructed, to pull initial LED modes from. + // We'll call setMode from setup() to get it to register. + + current_mode_p = &patternSelector; +} + +void Editor::setMode(EditorMode newmode) +{ + switch(newmode) + { + case eMODE_VOICE_SEL: + { + current_mode_p->setLEDs(false); + current_mode_p = &voiceSelector; + current_mode_p->setLEDs(true); + break; + } + case eMODE_MUTE_SEL: + { + current_mode_p->setLEDs(false); + current_mode_p = &muteSelector; + current_mode_p->setLEDs(true); + break; + } + case eMODE_PATT_SEL: + { + current_mode_p->setLEDs(false); + current_mode_p = &patternSelector; + current_mode_p->setLEDs(true); + break; + } + case eMODE_UTILITY: + { + current_mode_p->setLEDs(false); + current_mode_p = &utilityMode; + current_mode_p->setLEDs(true); + break; + } + case eMODE_CHAIN_EDIT: + { + current_mode_p->setLEDs(false); + current_mode_p = &chainEditor; + current_mode_p->setLEDs(true); + break; + } + case eMODE_STEP_ACCENT: + { + current_mode_p->setLEDs(false); + current_mode_p = &accentEditor; + current_mode_p->setLEDs(true); + break; + } + + default: + { + current_mode_p->setLEDs(false); + current_mode_p = &stepEditor; + current_mode_p->setLEDs(true); + break; + } + } + +} + +void Editor::receiveKey(uint32_t keynum, bool pressed) +{ +#if 0 + Serial.print("Ed Key: "); + Serial.print(keynum, HEX); + Serial.print(" "); + Serial.println(pressed); +#endif + + current_mode_p->HandleKey(keynum, pressed); +} + +void Editor::forceLEDs() +{ + current_mode_p->setLEDs(true); +} diff --git a/Software/editor.h b/Software/editor.h new file mode 100644 index 0000000..6398977 --- /dev/null +++ b/Software/editor.h @@ -0,0 +1,43 @@ +#ifndef _EDITOR_H_ +#define _EDITOR_H_ + +#pragma once + +#include + +// fwd decl for other headers +class Editor; + +#include "panel-scanner.h" +#include "editor-modes.h" + +class Editor +{ +public: + + enum EditorMode + { + eMODE_STEP_EDIT = 0, + eMODE_STEP_ACCENT, + eMODE_VOICE_SEL, + eMODE_MUTE_SEL, + eMODE_PATT_SEL, + eMODE_CHAIN_EDIT, + eMODE_UTILITY + }; + + Editor(); + + void receiveKey(uint32_t keynum, bool pressed); + void setMode(EditorMode); + void forceLEDs(); + +private: + + pvEditorMode* current_mode_p; + +}; + + + +#endif // keepout diff --git a/Software/panel-scanner.cpp b/Software/panel-scanner.cpp new file mode 100644 index 0000000..2cc615f --- /dev/null +++ b/Software/panel-scanner.cpp @@ -0,0 +1,385 @@ +#include +#include + +#include "panel-scanner.h" +#include "player.h" + +static const int32_t CHIPSEL_BTNS = 8; +static const int32_t CHIPSEL_LEDS = 5; + +extern Editor theEditor; +extern Player thePlayer; + +static const SPISettings registersettings(100000, MSBFIRST, SPI_MODE0 ); + + +// constructor... +PanelScanner::PanelScanner() +{ + for (uint32_t i = 0; i < NUM_PANELS; i++) + { + led_half_buffer[i] = 0; + led_background_buffer[i] = 0; + led_blinking_buffer[i] = 0; + led_overlay_buffer[i] = 0; + old_buttons[i] = 0; + new_buttons[i] = 0; + //setBlinkingLED(22,true); + } + +} + +void PanelScanner::initScanning() +{ + // SPI is shared with SD card, initialized in setup() + SPI.setSCK(14); + SPI.setMOSI(7); + SPI.setMISO(12); + SPI.begin(); + + pinMode(CHIPSEL_BTNS, OUTPUT); + digitalWrite(CHIPSEL_BTNS, LOW); + pinMode(CHIPSEL_LEDS, OUTPUT); + digitalWrite(CHIPSEL_LEDS, LOW); + +} + +void PanelScanner::tick() +{ + doTransaction(); + parseButtons(); +} + + +void PanelScanner::clearAllLED() +{ + for (uint32_t i = 0; i < NUM_PANELS; i++) + { + led_half_buffer[i] = 0; + led_background_buffer[i] = 0; + led_blinking_buffer[i] = 0; + led_overlay_buffer[i] = 0; + } +} + + +void PanelScanner::setHalfLED(uint32_t num, bool on) +{ + uint32_t byte_idx, bit_num; + +#if 0 + Serial.print("set half: "); + Serial.print(num); + Serial.print(" "); + Serial.println(on); +#endif + + if (!on) + { + clearHalfLED(num); + return; + } + + // Funny math at play here. + // LEDs are out of order WRT the buttons...the first bit shifted in is the + // last bit shifted out on the SPI ring. + // So we'll flop that around here by doing the shifting and indexing + // math from the top down, rather than bottom up. + + if (num < (NUM_PANELS * 8)) + { + byte_idx = NUM_PANELS - 1 - (num / 8); + bit_num = num % 8; + + led_half_buffer[byte_idx] |= 0x80 >> bit_num; + } +} + +void PanelScanner::clearHalfLED(uint32_t num) +{ + uint32_t byte_idx, bit_num; + + // see note above. + + if (num < (NUM_PANELS * 8)) + { + byte_idx = NUM_PANELS - 1 - (num / 8); + bit_num = num % 8; + + led_half_buffer[byte_idx] &= ~(0x80 >> bit_num); + } +} + +void PanelScanner::clearAllHalfLEDs() +{ + for (uint32_t i = 0; i < NUM_PANELS; i++) + { + led_half_buffer[i] = 0; + } +} + + + +void PanelScanner::setBackgroundLED(uint32_t num, bool on) +{ + uint32_t byte_idx, bit_num; + +#if 0 + Serial.print("set background: "); + Serial.print(num); + Serial.print(" "); + Serial.println(on); +#endif + + if (!on) + { + clearBackgroundLED(num); + return; + } + + // Funny math at play here. + // LEDs are out of order WRT the buttons...the first bit shifted in is the + // last bit shifted out on the SPI ring. + // So we'll flop that around here by doing the shifting and indexing + // math from the top down, rather than bottom up. + + if (num < (NUM_PANELS * 8)) + { + byte_idx = NUM_PANELS - 1 - (num / 8); + bit_num = num % 8; + + led_background_buffer[byte_idx] |= 0x80 >> bit_num; + } +} + +void PanelScanner::clearBackgroundLED(uint32_t num) +{ + uint32_t byte_idx, bit_num; + + // see note above. + + if (num < (NUM_PANELS * 8)) + { + byte_idx = NUM_PANELS - 1 - (num / 8); + bit_num = num % 8; + + led_background_buffer[byte_idx] &= ~(0x80 >> bit_num); + } +} + +void PanelScanner::clearAllBackgroundLEDs() +{ + for (uint32_t i = 0; i < NUM_PANELS; i++) + { + led_background_buffer[i] = 0; + } +} + +////////////////////////////////////////////// +void PanelScanner::setBlinkingLED(uint32_t num, bool on) +{ + uint32_t byte_idx, bit_num; + +#if 0 + Serial.print("set blinking: "); + Serial.println(num); +#endif + + if (!on) + { + clearBlinkingLED(num); + return; + } + + // Funny math at play here. + // LEDs are out of order WRT the buttons...the first bit shifted in is the + // last bit shifted out on the SPI ring. + // So we'll flop that around here by doing the shifting and indexing + // math from the top down, rather than bottom up. + + if (num < (NUM_PANELS * 8)) + { + byte_idx = NUM_PANELS - 1 - (num / 8); + bit_num = num % 8; + + led_blinking_buffer[byte_idx] |= 0x80 >> bit_num; + } +} + +void PanelScanner::clearBlinkingLED(uint32_t num) +{ + uint32_t byte_idx, bit_num; + + // see note above. + + if (num < (NUM_PANELS * 8)) + { + byte_idx = NUM_PANELS - 1 - (num / 8); + bit_num = num % 8; + + led_blinking_buffer[byte_idx] &= ~(0x80 >> bit_num); + } +} + +void PanelScanner::clearAllBlinkingLEDs() +{ +#if 0 + Serial.println("Clear all blinking"); +#endif + for (uint32_t i = 0; i < NUM_PANELS; i++) + { + led_blinking_buffer[i] = 0; + } +} +/////////////////////////////////// + +void PanelScanner::setOverlayLED(uint32_t num) +{ + uint32_t byte_idx, bit_num; + +#if 0 + Serial.print("OL on #"); + Serial.println(num); +#endif + + // Funny math at play here. + // LEDs are out of order WRT the buttons...the first bit shifted in is the + // last bit shifted out on the SPI ring. + // So we'll flop that around here by doing the shifting and indexing + // math from the top down, rather than bottom up. + + if (num < (NUM_PANELS * 8)) + { + byte_idx = NUM_PANELS - 1 - (num / 8); + bit_num = num % 8; + + led_overlay_buffer[byte_idx] |= 0x80 >> bit_num; + } +} + +void PanelScanner::clearOverlayLED(uint32_t num) +{ + uint32_t byte_idx, bit_num; + + // see note above. + + if (num < (NUM_PANELS * 8)) + { + byte_idx = NUM_PANELS - 1 - (num / 8); + bit_num = num % 8; + + led_overlay_buffer[byte_idx] &= ~(0x80 >> bit_num); + } +} + +void PanelScanner::clearAllOverlayLEDs() +{ + for (uint32_t i = 0; i < NUM_PANELS; i++) + { + led_overlay_buffer[i] = 0; + } + +} + + +void PanelScanner::parseButtons() +{ + uint8_t diff; + + for (uint32_t i = 0; i < NUM_PANELS; i++) + { + // in each byte, see if anything changed from last invocation + diff = new_buttons[i] ^ old_buttons[i]; + + for (uint32_t j = 0, mask = 0x01; j < 8; j++, mask <<= 1) + { + if (diff & mask) + { + //Serial.println(new_buttons[i]); + theEditor.receiveKey((i * 8) + j, (new_buttons[i] & mask)); + } + } + + // then store the new state for comparison next time + old_buttons[i] = new_buttons[i]; + } +} + +void PanelScanner::dumpLEDs() +{ + Serial.print(led_background_buffer[0], HEX); + Serial.print(led_background_buffer[1], HEX); + Serial.println(led_background_buffer[2], HEX); + + Serial.print(led_blinking_buffer[0], HEX); + Serial.print(led_blinking_buffer[1], HEX); + Serial.println(led_blinking_buffer[2], HEX); + + Serial.print(led_overlay_buffer[0], HEX); + Serial.print(led_overlay_buffer[1], HEX); + Serial.println(led_overlay_buffer[2], HEX); +} + + +void PanelScanner::doTransaction() +{ + uint32_t i; + //static uint32_t count = 0; + uint8_t trans_buffer[NUM_PANELS]; + + //count++; + + // Ask the player for 1/32 step info for sync'd blinking + bool blink_phase = thePlayer.getBlinkPhase(); + + for (i = 0; i < NUM_PANELS; i++) + { + if (blink_phase) + { + trans_buffer[i] = led_half_buffer[i]; + } + else + { + trans_buffer[i] = 0; + } + + trans_buffer[i] |= led_background_buffer[i]; + trans_buffer[i] ^= led_overlay_buffer[i]; + + // Blinking supersedes static state...if blinking, it should blink, + // regardless of underlying static setting. + if (blink_phase) + { + trans_buffer[i] |= led_blinking_buffer[i]; + } + else + { + trans_buffer[i] &= ~led_blinking_buffer[i]; + + } + } + + //LEDs korrekt + //S1-S8 und S17-S24 tauschen + + SPI.beginTransaction(registersettings); + + digitalWrite(CHIPSEL_BTNS, HIGH); + + SPI.transfer(trans_buffer, NUM_PANELS); + + digitalWrite(CHIPSEL_LEDS, HIGH); + digitalWrite(CHIPSEL_BTNS, LOW); + digitalWrite(CHIPSEL_LEDS, LOW); + + SPI.endTransaction(); + + + for (i = 0; i < NUM_PANELS; i++) + { + // Button lines are pulled high by resistors, shorted + // to ground when button is pressed. + // Invert what we read to make active high + new_buttons[i] = ~trans_buffer[2-i]; + } +}