This is a part of the Kinetiszer project.

Dependencies:   inc

Dependents:   kinetisizer

arpeggiator.c

Committer:
Clemo
Date:
2014-10-28
Revision:
1:8ae4ab73ca6a
Parent:
0:cb80470434eb

File content as of revision 1:8ae4ab73ca6a:

/*
Copyright 2013 Paul Soulsby www.soulsbysynths.com
    This file is part of Atmegatron.

    Atmegatron is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Atmegatron is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Atmegatron.  If not, see <http://www.gnu.org/licenses/>.
*/


#include "atmegatron.h"

#ifdef __HAS_ARPEGGIATOR__

//****THIS IS WHERE THE ARPEGGIATOR PATTERNS ARE STORED*******
//The way its is by up the notes currently being held down
//So if C,D,E,F,G  were held down and pattern was {5,3,2,4,1},  
//the arp would be G,E,D,F,C.  Minus nums count downwards


const signed char arp_patstore[16][16] =
{
  {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},                          //THIS ONE SHOULD ALWAYS BE BLANK
  {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16},                   //up
  {-1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16},   //down
  {1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8},                          //octave
  {1,1,2,3,4,4,5,6,7,7,8,9,10,10,11,12},                      //5ths
  {1,3,2,4,3,5,4,6,5,7,6,8,7,9,8,10},                         //skip 1 up
  {-1,-3,-2,-4,-3,-5,-4,-6,-5,-7,-6,-8,-7,-9,-8,-10},         //skip 1 down
  {-1,-1,-1,-1,-3,-3,-2,-2,-1,-1,-1,-1,-3,-3,-2,-2},          //I feel love
  {2,1,3,2,4,5,6,1,2,1,3,2,4,5,6,1},                          //Horton
  {-1,-2,-1,-4,-2,-1,-4,-1,-4,-2,-3,-2,-1,-3,-2,-1},          //Etheridge
  {1,2,3,2,4,3,2,3,1,3,2,4,1,3,2,1},                          //Meads
  {1,2,3,2,-1,6,5,4,0,0,-1,-2,0,0,4,0},                       //Palmer
  {4,1,4,1,3,1,4,1,2,1,3,1,4,1,4,1},                          //gwEm 1
  {1,1,4,4,1,1,4,4,3,3,6,6,2,2,5,5},                          //gwEm 2 
  {1,1,3,2,2,4,3,3,5,4,4,6,5,5,7,6},                          //Triplet up
  {1,2,3,2,1,2,3,2,1,2,3,2,1,2,3,2},                          //You Will Be Robyn
};

//local vars
unsigned long arp_ticksperstep = 250;      //ticks each step of arp should hold for
unsigned long arp_nexttick = 0;            //tick when next step fires
unsigned long arp_curtick = 0;             //current tick
unsigned long arp_starttick = 0;           //start tick
   
byte arp_curpos = 0;                       //current position in pattern
byte arp_curnote = 255;                    //current MIDI note in pattern
boolean arp_pongdown = false;              //current direction if in ping-pong mode

//lets and gets
boolean arp_pingpong = false;
byte arp_curpat = 0;
unsigned int arp_speed = 8191;


//lets and gets
void Arp_Let_Type(byte newpat)
{             //set pattern number (0-15)
  //byte i;
  if (newpat!=arp_curpat){                  //if new pattern is different to current
    arp_curpat = newpat;                    //set it
    Arp_Reset();                            //Reset vars for safety
  }
}


byte Arp_Get_Type(void)
{
  return arp_curpat;
}


void Arp_Let_Speed(unsigned int newspeed)
{                                  //Set speed (speed is stored as an integer multiplier (speed_store on hardware page))
  //unsigned long cticks;
  arp_speed = newspeed;
  arp_ticksperstep = ((unsigned long)arp_speed+1) * MASTER_BEATTICK >> 13;  //multiplier * ticks-per-beat (500ms=120bpm) / 8192.  This is a way of not storing speed as floats 
}


unsigned int Arp_Get_Speed(void)
{
  return arp_speed;
}


void Arp_Let_PingPong(boolean newpingpong)
{  //set ping-pong mode.   This runs sequence forwards, backwards, forwards, backwards etc etc
  arp_pingpong = newpingpong;
}


boolean Arp_Get_PingPong(void)
{
  return arp_pingpong;
}


//reset all local vars before starting arp
void Arp_Reset(void)
{
  arp_curpos = 0;               //restart pattern at start
  arp_curnote = 0;              //restart note search at bottom
  arp_starttick = master_tick;  //set start tick
  arp_curtick = 0;              //reset current tick
  arp_nexttick = 0;             //reset current tick
}


//Poll arpegiator and see if it's time for a new note to fire
void Arp_Poll(void)
{
	// is MIDI clock off and pattern <> 0 (MIDI_ClockTick() handles arpeggiator when clock is present)
	if (MIDI_Get_ClockPresent()==false && arp_curpat>0)
	{
		//is it time for next note?
		if (arp_curtick>=arp_nexttick)
		{
			if (Hardware_Get_Ctrl_Shift()==GREEN)
			{
				Board_LED_Toggle(BOARD_LED1_GREEN);
				Board_LED_Set(BOARD_LED2_GREEN,!Board_LED_Test(BOARD_LED1_GREEN));
			}
			else
			{
				Board_LED_Toggle(BOARD_LED1_RED);
				Board_LED_Set(BOARD_LED2_RED,!Board_LED_Test(BOARD_LED1_RED));
			}
			//SysTick_LED_Flash(BOARD_LED1_GREEN,10);
			do
			{
				Arp_TriggerStep();  //fire note
				arp_nexttick += arp_ticksperstep;  //set the next tick for a note
			}
			while (arp_nexttick<arp_curtick);  //in a nasty do loop because there's a chance the poll time is slower than arp_ticksperstep
		}
		arp_curtick = master_tick - arp_starttick;  //refresh current tick
	}
}


void Arp_TriggerStep(void)
{                                                                  //fire note in pattern
  arp_curnote = Arp_FindNote(arp_patstore[arp_curpat][arp_curpos]);    //attempt to find a note to fire
  if (arp_curnote==255){                                                                 //255 = failed to find note
    if (arp_pingpong==true){                                                             //if in ping-pong mode
      if (arp_pongdown==false){                                                          //probably coz its hit the end of pattern and needs to flip direction
        arp_curpos -= 2;                                                                 //flip direction and set position
        arp_pongdown = true;
      }
      else{
        arp_curpos += 2;
        arp_pongdown = false;
      }        
    }
    else{                                                                                //if not ping-pong, give up and reset to start of pattern
      arp_curpos = 0;
    }
    arp_curnote = Arp_FindNote(arp_patstore[arp_curpat][arp_curpos]);  //have another stab at finding a note now
  }
  if (arp_curnote!=255){                                                                 //assuming it now has found as note
    MIDI_TriggerNote(arp_curnote);                                                       //fire it
  }          
  Arp_IncPatPos();                                                                       //increment current position in pattern  (more complex than just arp_curpos++)
}


/*int Arp_FindNoteUp(int count)
{
	int n = 0;
	while (count>0)
	{
		while (n<128)
		{
			n++;
			if (MIDI_Get_KeyDown(n)==true)
			{
				break;
			}
		}
		count--;
	}
	return n<128? n : 255;
}


int Arp_FindNoteDown(int count)
{
	int n = 127;
	while (count>0)
	{
		while (n>0)
		{
			n--;
			if (MIDI_Get_KeyDown(n)==true)
			{
				break;
			}
		}
		count--;
	}
	return n>0? n : 255;
}


//this finds out which note to fire, based on which notes are held down
byte Arp_FindNote(signed char pos)
{
	user_interface_print_value(1,0,pos,false);
	if (pos<0)
	{
		return Arp_FindNoteDown(-pos);
	}
	if (pos>0)
	{
		return Arp_FindNoteUp(pos);
	}
	return 255;
}*/


byte Arp_FindNote(signed char pos)
{
  byte i,j,n,l,o;
  //set to unused value = next note not found
  o = 255;
  if (pos<0)
  {
	    //set start of search at top or bottom of keyboard (at the moment you can't arpeggiate top or bottom note)
	n = 127;
  }
  else
  {
    n = 0;
  }
  l = abs(pos);
  //now track up/down keyboard finding notes.  loop l times, setting o=note if note is down
  for (i=0; i<l; i++)
  {
    for (j=0; j<128; j++)
    {
      if (pos>0)
      {
        if (n<127)
        {
          n++;
        }
        else
        {
          o = 255;
          break;
        }
      }
      else if (pos<0)
      {
        if (n>0)
        {
          n--;
        }
        else
        {
          o = 255;
          break;
        }
      }
      else
      {
        break;  //safety
      }
      if (MIDI_Get_KeyDown(n)==true)
      {
        o = n;
        break;
      }
    }
  }
  //return o.  It'll either be 255 if failed to find note, or the note num.
  return o;
}


//increments arp_curpos
void Arp_IncPatPos(void)
{                
  if (arp_pingpong==false){        //if not in ping-pong mode
    if (arp_curpos==PATLEN-1){     //if at end of pattern
      arp_curpos = 0;              //restart
    }
    else{
      arp_curpos++;                //otherwise increment
    }
  }
  else{                            //if in ping-pong  
    if (arp_pongdown==false){      //and heading up through pattern
      if (arp_curpos>=PATLEN-1){   //if hit end of pattern flip direction
        arp_curpos--;
        arp_pongdown = true;
      }
      else{
        arp_curpos++;              //otherwise decrement
      }        
    }
    else{                          //if heading down the pattern
      if (arp_curpos<=0){          //and hit the start, flip direction
        arp_curpos++;
        arp_pongdown = false;
      }
      else{
        arp_curpos--;              //otherwise decrement
      }
    }
  }
}


#endif // __HAS_ARPEGGIATOR__