#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; } }