diff --git a/Software/TeensyBoom_Midi.ino b/Software/TeensyBoom_Midi.ino new file mode 100644 index 0000000..cfac9a9 --- /dev/null +++ b/Software/TeensyBoom_Midi.ino @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "panel-scanner.h" +#include "editor.h" +#include "pattern.h" +#include "player.h" +#include "voice.h" +#include "teensy_midi.h" + +const int channel = 1; + +// Appp construct singletons +PanelScanner theScanner; +Editor theEditor; +Pattern thePattern; // TBD: multidimensional... +Player thePlayer; +Teensy_Midi theMidi; + + +void setup() { + // put your setup code here, to run once: + + Serial.begin(57600); + + delay(1500); + + Serial.println("Setup"); + + pinMode(15, INPUT); // Volume pot pin? + + // SD CARD & general SPI inits + // Initialize the SD card + SPI.setMOSI(7); + SPI.setSCK(14); + + // This sets the chip selects for the panel + // to OFF. Gotta do it before SD init, else the panels contend! + theScanner.initScanning(); + + + if (!(SD.begin(10))) + { + Serial.println("Unable to access the SD card"); + } + else + { + Serial.println("SD card begin worked"); + } + + if (SD.exists("test.txt")) + { + Serial.println("found test.txt file"); + } + else + { + Serial.println("Didn't find test file?"); + } + + theMidi.init(); //Midi Initialisieren + theEditor.setMode(Editor::eMODE_PATT_SEL); + + // audio library init + AudioMemory(20); + + //next = millis() + 1000; + + // read panel before we start to run + //paramUpdate(); + + voiceInit(); + + + delay(500); + + Serial.println("Setup Complete"); +} + +void loop() +{ + uint32_t now = millis(); + static uint32_t then; + +#if 1 + // put your main code here, to run repeatedly: + + paramUpdate1();// kik,snr,hat + paramUpdate2();// toms, shaker + paramUpdate3();// master volume & tempo + + if (now > then) + { + thePlayer.tick(); + } + + if (now % 5 == 0) + { + theScanner.tick(); + } + + if (now % 5000 == 0) + { + //theScanner.dumpLEDs(); + + + Serial.print("Diagnostics: "); + Serial.print(" max, buffs: "); + Serial.print(AudioProcessorUsageMax()); + Serial.print(" "); + Serial.println(AudioMemoryUsageMax()); + AudioProcessorUsageMaxReset(); + //Test: + // triggerKick(true); + // int note = 40; +// for (note=10; note <= 127; note++) { +// MIDI.sendNoteOn(note, 100, channel); +// delay(200); +// MIDI.sendNoteOff(note, 100, channel); +// setBlinkingLED(22, true); + //setLEDs(true); +// } + } + + then = now; +#endif +} diff --git a/Software/player.cpp b/Software/player.cpp new file mode 100644 index 0000000..78d5b8a --- /dev/null +++ b/Software/player.cpp @@ -0,0 +1,379 @@ +#include + +#include "player.h" +#include "pattern.h" +#include "editor.h" +#include "voice.h" +#include "teensy_midi.h" + +// Similar, for the pattern... +extern Pattern thePattern; +extern PanelScanner theScanner; +extern Editor theEditor; +extern Teensy_Midi theMidi; + +// constructor... +Player::Player() +{ + playing = false; + swing = false; + current_step = 0; + + active_mutes = 0; + pending_mutes = 0; + + active_pattern = 0; + pending_pattern = -1; + + pause_len = 125;// milliseconds, 125 mS = 120 bpm +} + +void Player::start() +{ + playing = true; + current_step = 0; + + if(chain_active) + { + chain_play_idx = 0; + active_pattern = chain_array[0]; + thePattern.setCurrentPattern(active_pattern); + } + + //theScanner.setOverlayLED(0x17); + theEditor.forceLEDs(); + + theMidi.start(); +} +void Player::stop() +{ + playing = false; + theEditor.forceLEDs(); + theMidi.stop(); +} + +bool Player::isPlaying() +{ + return playing; +} + +void Player::setPause(uint32_t millisec) +{ + pause_len = millisec; +} + +bool Player::toggleSwing() +{ + swing = !swing; + + Serial.print("Swing is now: "); + Serial.println(swing); + + return swing; +} + +bool Player::getSwing() +{ + return swing; +} + +bool Player::toggleMuteBit(uint32_t bit) +{ + if(bit > 12) + return false; + + if(playing) + { + pending_mutes ^= (1 << bit); + + return (pending_mutes & (1 << bit)); + } + else + { + active_mutes ^= (1 << bit); + + return (active_mutes & (1 << bit)); + } +} + +bool Player::getMuteBit(uint32_t bit) +{ + if(bit > 12) + return false; + + return (active_mutes & (1 << bit)); +} + +bool Player::getPendingMuteBit(uint32_t bit) +{ + if(bit > 12) + return false; + + return (pending_mutes & (1 << bit)); +} + + + +bool Player::setNextPattern(int32_t next) +{ + if((uint32_t) next > Pattern::NUM_PATTERNS) + return false; + + if(next == active_pattern) + { + return false; + } + + if(playing) + { + pending_pattern = next; + + return true;// (pending_pattern); + } + else + { + active_pattern = next; + thePattern.setCurrentPattern(active_pattern); + + return true; //(active_mutes & (1 << bit)); + } +} + +int32_t Player::getActivePattern() +{ + return active_pattern; +} + +int32_t Player::getPendingPattern() +{ + return pending_pattern; +} + + +int32_t Player::getCurrentStep() +{ + return current_step; +} + +bool Player::getBlinkPhase() +{ + int32_t now = millis(); + + if(now < (next_time - (pause_len>>1))) + { + return true; + } + return false; + +} + +//////////////////////////////////////////////////////////////// +// chain stuff +//////////////////////////////////////////////////////////////// + +bool Player::chainIsActive() +{ + return chain_active; +} + +void Player::initChain() +{ + chain_active = false; + chain_insert_idx = 0; + chain_play_idx = 0; +} + +void Player::addToChain(int32_t patt_num) +{ + if((uint32_t) patt_num >= Pattern::NUM_PATTERNS) + { + Serial.println("addToChain patt_num out of bounds!"); + return; + } + + Serial.print("Adding patt "); + Serial.print(patt_num); + Serial.print("to chain at idx "); + Serial.println(chain_insert_idx); + + if(!chain_active) + { + // the first piece added to chain + chain_active = true; + chain_array[0] = patt_num; + chain_insert_idx = 1; + chain_play_idx = -1;// one before the first step - it'll increment and be at beginning + theEditor.forceLEDs(); + return; + } + + if(chain_insert_idx < (int32_t)CHAIN_LEN) + { + chain_array[chain_insert_idx] = patt_num; + chain_insert_idx++; + theEditor.forceLEDs(); + } + +} + +int32_t Player::getNextChainVal() +{ + chain_play_idx++; + if(chain_play_idx >= chain_insert_idx) + { + chain_play_idx = 0; + } + +#if 0 + Serial.print("Getting next chain pattern, (idx, val):"); + Serial.print(chain_play_idx); + Serial.print(" "); + Serial.println(chain_array[chain_play_idx]); +#endif + + return chain_array[chain_play_idx]; +} + +bool Player::checkChainMembership(int32_t patt) +{ + // is patt # active within chain? + for(int32_t i = 0; i < chain_insert_idx; i++) + { + if(patt == chain_array[i]) + { + //Serial.println("found chain member"); + return true; + } + } + return false; +} + +void Player::tick() +{ + int32_t now = millis(); + + if(now < next_time) + { + return; + } + + if(!swing) + { + next_time = now + pause_len; + } + else + { + if(!(current_step & 0x01)) + { + next_time = now + pause_len + (pause_len/3); + } + else + { + next_time = now + ((2*pause_len)/3); + } + } + + if(playing) + { + uint32_t trigdata = thePattern.getStepData(current_step); + + // Apply mutes + trigdata &= (~active_mutes); + + theEditor.forceLEDs(); + +#if 0 + Serial.print("Trigger: step#"); + Serial.print(current_step); + Serial.print(" bitmap:"); + Serial.println(trigdata, HEX); +#endif + + theMidi.clock(); + + + AudioNoInterrupts(); + + if (trigdata & 0x01) + { + triggerKick(trigdata & 0x010000); + } + if (trigdata & 0x02) + { + triggerSnare(trigdata & 0x020000); + } + if (trigdata & 0x04) + { + // closed hat trumps open hat. + triggerHat(false, trigdata & 0x040000); + } + else if (trigdata & 0x08) + { + triggerHat(true, trigdata & 0x080000); + } + if (trigdata & 0x10) + { + triggerTom(1, trigdata & 0x100000); + } + else if (trigdata & 0x20) + { + triggerTom(2, trigdata & 0x200000); + } + else if (trigdata & 0x40) + { + triggerTom(3, trigdata & 0x400000); + } + if (trigdata & 0x80) + { + triggerBell(trigdata & 0x800000); + } + if (trigdata & 0x100) + { + triggerShaker(trigdata & 0x1000000); + } + if (trigdata & 0x200) + { + triggerCymbal(trigdata & 0x2000000); + } + if (trigdata & 0x400) + { + triggerClap(trigdata & 0x4000000); + } + AudioInterrupts(); + } + + current_step++; + if(current_step >= 0x10) + { + // this block of code is setting up the NEXT invocation, so we can change + // patterns without overhead before the next set of triggers. + // It means that come instenal state is ahead of the actual playback state - + // the takeaway: don't call theEditor.forceLEDs(), or the display gets out of sync + + current_step = 0; + if(pending_mutes) + { + active_mutes ^= pending_mutes; + pending_mutes = 0; + } + + if(!chain_active) + { + if(pending_pattern != -1) + { + active_pattern = pending_pattern; + thePattern.setCurrentPattern(pending_pattern); + pending_pattern = -1; + } + //else, just keep current pattern... + } + else//chain_active + { + active_pattern = getNextChainVal(); + thePattern.setCurrentPattern(active_pattern); + } + + // do this after the LED updates, or they get the wrong current step! + current_step = 0; + } +} diff --git a/Software/player.h b/Software/player.h new file mode 100644 index 0000000..f349b9e --- /dev/null +++ b/Software/player.h @@ -0,0 +1,68 @@ +#ifndef _PLAYER_H_ +#define _PLAYER_H_ + +#pragma once + +class Player +{ +public: + Player(); + + void start(); + void stop(); + + bool isPlaying(); + + void setPause(uint32_t millisec); + + bool toggleSwing(); + bool getSwing(); + + bool toggleMuteBit(uint32_t bit); + bool getMuteBit(uint32_t bit); + bool getPendingMuteBit(uint32_t bit); + + bool setNextPattern(int32_t next); + int32_t getActivePattern(); + int32_t getPendingPattern(); + + int32_t getCurrentStep(); + bool getBlinkPhase(); + + void tick(); + + static const uint32_t CHAIN_LEN = 32; + + // Chain Stuff + bool chainIsActive(); + void initChain(); + void addToChain(int32_t patt_num); + int32_t getNextChainVal(); + bool checkChainMembership(int32_t patt); + +private: + + bool playing; + bool swing; + int32_t current_step; + int32_t prev_step; + int32_t pause_len; + int32_t next_time; + + int32_t active_mutes; + int32_t pending_mutes; + + int32_t active_pattern; + int32_t pending_pattern; + + bool chain_active; + int32_t chain_len; + int32_t chain_insert_idx; + int32_t chain_play_idx; + int8_t chain_array[CHAIN_LEN]; + +}; + + + +#endif // keepout diff --git a/Software/teensy_midi.cpp b/Software/teensy_midi.cpp new file mode 100644 index 0000000..9024678 --- /dev/null +++ b/Software/teensy_midi.cpp @@ -0,0 +1,40 @@ +#include +#include "teensy_midi.h" + +MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); +int MIDI_Clock = 1; +int MIDI_Clock_Master = 1; + +// constructor... +Teensy_Midi::Teensy_Midi() +{ +} + +void Teensy_Midi::init() +{ +MIDI.begin(); +Serial.print("Midi Begin: "); +} + + +void Teensy_Midi::start() +{ + if (MIDI_Clock == 1 && MIDI_Clock_Master == 1) { + MIDI.send((midi::MidiType)0xFA, 0, 0, 1); + } +} + +void Teensy_Midi::stop() +{ + if (MIDI_Clock == 1 && MIDI_Clock_Master == 1) { + MIDI.send((midi::MidiType)0xFC, 0, 0, 1); + } +} + +void Teensy_Midi::clock() +{ + //Send midi-CLock + if (MIDI_Clock == 1 && MIDI_Clock_Master == 1) { + MIDI.send((midi::MidiType)0xF8, 0, 0, 1); + } +} diff --git a/Software/teensy_midi.h b/Software/teensy_midi.h new file mode 100644 index 0000000..70c702f --- /dev/null +++ b/Software/teensy_midi.h @@ -0,0 +1,17 @@ +#ifndef _TEENSY_MIDI_H_ +#define _TEENSY_MIDI_H_ + +class Teensy_Midi +{ +public: + Teensy_Midi(); + + void init(); + + void start(); + void stop(); + + void clock(); +}; + +#endif