TeensyBoom/Software/player.cpp

380 lines
6.5 KiB
C++

#include <Arduino.h>
#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;
}
}