This is a part of the Kinetiszer project.

Dependencies:   inc

Dependents:   kinetisizer

Committer:
Clemo
Date:
Tue Oct 28 20:09:12 2014 +0000
Revision:
1:8ae4ab73ca6a
Parent:
0:cb80470434eb
First publication (untested)

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Clemo 0:cb80470434eb 1 /*
Clemo 0:cb80470434eb 2 Copyright 2013 Paul Soulsby www.soulsbysynths.com
Clemo 0:cb80470434eb 3 This file is part of Atmegatron.
Clemo 0:cb80470434eb 4
Clemo 0:cb80470434eb 5 Atmegatron is free software: you can redistribute it and/or modify
Clemo 0:cb80470434eb 6 it under the terms of the GNU General Public License as published by
Clemo 0:cb80470434eb 7 the Free Software Foundation, either version 3 of the License, or
Clemo 0:cb80470434eb 8 (at your option) any later version.
Clemo 0:cb80470434eb 9
Clemo 0:cb80470434eb 10 Atmegatron is distributed in the hope that it will be useful,
Clemo 0:cb80470434eb 11 but WITHOUT ANY WARRANTY; without even the implied warranty of
Clemo 0:cb80470434eb 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Clemo 0:cb80470434eb 13 GNU General Public License for more details.
Clemo 0:cb80470434eb 14
Clemo 0:cb80470434eb 15 You should have received a copy of the GNU General Public License
Clemo 0:cb80470434eb 16 along with Atmegatron. If not, see <http://www.gnu.org/licenses/>.
Clemo 0:cb80470434eb 17 */
Clemo 0:cb80470434eb 18
Clemo 0:cb80470434eb 19 #include "atmegatron.h"
Clemo 0:cb80470434eb 20
Clemo 0:cb80470434eb 21
Clemo 0:cb80470434eb 22 //local variables
Clemo 0:cb80470434eb 23 unsigned long porta_starttick; //portamento - tick glide started
Clemo 0:cb80470434eb 24 unsigned long porta_curtick; //portamento - current tick through glide
Clemo 0:cb80470434eb 25 byte pitch_lfolookup[256]; //look up table to convert lfo output to pitch multiplier. Lookup saves having to do v slow exp calculation every cycle
Clemo 0:cb80470434eb 26 byte pitch_fenvlookup[256]; //look up table to convert env output to pitch multiplier. Lookup saves having to do v slow exp calculation every cycle
Clemo 0:cb80470434eb 27 byte pitch_pbendlookup; //look up value to convert pitch bend to pitch multiplier. Lookup saves having to do v slow exp calculation every cycle. Don't need table, because it's a fixed value
Clemo 0:cb80470434eb 28 float pitch_lfoamtf = 0; //lfo amount as float (0-1)
Clemo 0:cb80470434eb 29 float pitch_fenvamtf = 0; //env amount as float (0-1)
Clemo 0:cb80470434eb 30 unsigned long pitch_lastfreq_calc = 220; //last value of pitch_freq_calc
Clemo 0:cb80470434eb 31 char pitch_lastpbend;
Clemo 0:cb80470434eb 32 boolean pitch_changed = true;
Clemo 0:cb80470434eb 33
Clemo 0:cb80470434eb 34 //lets and gets
Clemo 0:cb80470434eb 35 unsigned int pitch_lastfreq = 220; //last frequency set
Clemo 0:cb80470434eb 36 unsigned int pitch_curfreq = 220; //current frequency (must never be 0)
Clemo 0:cb80470434eb 37 unsigned int pitch_nextfreq = 220; //next frequency. Frequency that current frequency is heading to (based on portamento level)
Clemo 0:cb80470434eb 38 unsigned int pitch_portaticks = 0; //portamento speed in ticks (ticks to glide from one note to another)
Clemo 0:cb80470434eb 39 unsigned int pitch_portaspeed = 0; //portamento speed in ticks not taking into account of distance between prev and next notes (propotional mode)
Clemo 0:cb80470434eb 40 boolean pitch_propporta = false; //portamento propotional mode
Clemo 0:cb80470434eb 41 byte pitch_lfoamt = 0; //amount lfo effects pitch
Clemo 0:cb80470434eb 42 byte pitch_fenvamt = 0; //amount env effects pitch
Clemo 0:cb80470434eb 43 unsigned long pitch_freq_calc = 220; //master current frequency (after lfo, env, pitch wheel calculated)
Clemo 0:cb80470434eb 44
Clemo 0:cb80470434eb 45
Clemo 0:cb80470434eb 46 //lets and gets
Clemo 0:cb80470434eb 47 void Pitch_Let_NextFreq(unsigned int newfreq)
Clemo 0:cb80470434eb 48 { //set next frequency - i.e. the freq that the portamento heads towards
Clemo 0:cb80470434eb 49 if (newfreq!=pitch_nextfreq){ //if new value != current, then update and refresh portamento
Clemo 0:cb80470434eb 50 pitch_nextfreq = newfreq;
Clemo 0:cb80470434eb 51 pitch_lastfreq = pitch_curfreq;
Clemo 0:cb80470434eb 52 Pitch_RefreshPortaTicks();
Clemo 0:cb80470434eb 53 }
Clemo 0:cb80470434eb 54 }
Clemo 0:cb80470434eb 55
Clemo 0:cb80470434eb 56
Clemo 0:cb80470434eb 57 unsigned int Pitch_Get_NextFreq(void)
Clemo 0:cb80470434eb 58 {
Clemo 0:cb80470434eb 59 return pitch_nextfreq;
Clemo 0:cb80470434eb 60 }
Clemo 0:cb80470434eb 61
Clemo 0:cb80470434eb 62
Clemo 0:cb80470434eb 63 void Pitch_Let_Porta(int newporta)
Clemo 0:cb80470434eb 64 { //set portamento speed. this is a preset value stored in hardware tab.
Clemo 0:cb80470434eb 65 if (newporta!=pitch_portaspeed){
Clemo 0:cb80470434eb 66 pitch_portaspeed = newporta;
Clemo 0:cb80470434eb 67 Pitch_RefreshPortaTicks(); //if proportional mode is on, the actual porta speed (portaticks) needs calculating.
Clemo 0:cb80470434eb 68 }
Clemo 0:cb80470434eb 69 }
Clemo 0:cb80470434eb 70
Clemo 0:cb80470434eb 71
Clemo 0:cb80470434eb 72 int Pitch_Get_Porta(void)
Clemo 0:cb80470434eb 73 {
Clemo 0:cb80470434eb 74 return pitch_portaspeed;
Clemo 0:cb80470434eb 75 }
Clemo 0:cb80470434eb 76
Clemo 0:cb80470434eb 77
Clemo 0:cb80470434eb 78 void Pitch_ResetPorta(void)
Clemo 0:cb80470434eb 79 {
Clemo 0:cb80470434eb 80 porta_starttick = master_tick;
Clemo 0:cb80470434eb 81 }
Clemo 0:cb80470434eb 82
Clemo 0:cb80470434eb 83
Clemo 0:cb80470434eb 84 void Pitch_RefreshPortaTicks(void)
Clemo 0:cb80470434eb 85 {
Clemo 0:cb80470434eb 86 unsigned long p;
Clemo 0:cb80470434eb 87 if (pitch_portaspeed==0){ //if portamento is off, speed = 0 ticks
Clemo 0:cb80470434eb 88 pitch_portaticks = 0;
Clemo 0:cb80470434eb 89 }
Clemo 0:cb80470434eb 90 else if (pitch_propporta==true) { //if propotional mode is on, speeed is proportional to diff between current and next frequencies
Clemo 0:cb80470434eb 91 if (pitch_nextfreq>pitch_curfreq){
Clemo 0:cb80470434eb 92 p = (unsigned long)(pitch_nextfreq-pitch_curfreq) * pitch_portaspeed / pitch_curfreq;
Clemo 0:cb80470434eb 93 }
Clemo 0:cb80470434eb 94 else{
Clemo 0:cb80470434eb 95 p = (unsigned long)(pitch_curfreq-pitch_nextfreq) * pitch_portaspeed / pitch_nextfreq;
Clemo 0:cb80470434eb 96 }
Clemo 0:cb80470434eb 97 pitch_portaticks = (unsigned int)p;
Clemo 0:cb80470434eb 98 }
Clemo 0:cb80470434eb 99 else{
Clemo 0:cb80470434eb 100 pitch_portaticks = pitch_portaspeed; //if proportional mode is off, speed is just = portaspeed
Clemo 0:cb80470434eb 101 }
Clemo 0:cb80470434eb 102 }
Clemo 0:cb80470434eb 103
Clemo 0:cb80470434eb 104
Clemo 0:cb80470434eb 105 unsigned long Pitch_Get_FreqCalc(void)
Clemo 0:cb80470434eb 106 {
Clemo 0:cb80470434eb 107 return pitch_freq_calc; //needed by interrupt loop to set frequency
Clemo 0:cb80470434eb 108 }
Clemo 0:cb80470434eb 109
Clemo 0:cb80470434eb 110
Clemo 0:cb80470434eb 111 boolean Pitch_Get_PitchChanged(void)
Clemo 0:cb80470434eb 112 { //has freq changed since last
Clemo 0:cb80470434eb 113 return pitch_changed;
Clemo 0:cb80470434eb 114 }
Clemo 0:cb80470434eb 115
Clemo 0:cb80470434eb 116
Clemo 0:cb80470434eb 117 void Pitch_Let_PropPorta(boolean newprop)
Clemo 0:cb80470434eb 118 { //set proportional portamento mode. refresh portaticks.
Clemo 0:cb80470434eb 119 if (newprop!=pitch_propporta){
Clemo 0:cb80470434eb 120 pitch_propporta = newprop;
Clemo 0:cb80470434eb 121 Pitch_RefreshPortaTicks();
Clemo 0:cb80470434eb 122 }
Clemo 0:cb80470434eb 123 }
Clemo 0:cb80470434eb 124
Clemo 0:cb80470434eb 125
Clemo 0:cb80470434eb 126 boolean Pitch_Get_PropPorta(void)
Clemo 0:cb80470434eb 127 {
Clemo 0:cb80470434eb 128 return pitch_propporta;
Clemo 0:cb80470434eb 129 }
Clemo 0:cb80470434eb 130
Clemo 0:cb80470434eb 131
Clemo 0:cb80470434eb 132 //************PITCH PROCESS****************
Clemo 0:cb80470434eb 133 void Pitch_Process(void)
Clemo 0:cb80470434eb 134 {
Clemo 0:cb80470434eb 135 unsigned long p;
Clemo 0:cb80470434eb 136
Clemo 0:cb80470434eb 137 if (pitch_curfreq!=pitch_nextfreq){ //is current frequency = next freq (i.e. the freq we want it to eventually be). first calculate the portamento
Clemo 0:cb80470434eb 138 if (pitch_portaticks==0){ //if portamento is off
Clemo 0:cb80470434eb 139 pitch_curfreq = pitch_nextfreq; //curfreq = next freq (no glide)
Clemo 0:cb80470434eb 140 }
Clemo 0:cb80470434eb 141 else{ //if portamento is on
Clemo 0:cb80470434eb 142 porta_curtick = master_tick - porta_starttick; //calc ticks passed
Clemo 0:cb80470434eb 143 if (porta_curtick>pitch_portaticks){ //curtick > portaticks, curfreq = next freq (i.e. glide has finished)
Clemo 0:cb80470434eb 144 pitch_curfreq = pitch_nextfreq;
Clemo 0:cb80470434eb 145 }
Clemo 0:cb80470434eb 146 else{ //otherwise
Clemo 0:cb80470434eb 147 pitch_curfreq = map(porta_curtick,0,pitch_portaticks,pitch_lastfreq,pitch_nextfreq); //map current frequency to ticks (uses arduino map function: curtick*(nextfreq - lastfreq) / portticks + lastfreq)
Clemo 0:cb80470434eb 148 }
Clemo 0:cb80470434eb 149 }
Clemo 0:cb80470434eb 150 }
Clemo 0:cb80470434eb 151 else if (pitch_lastfreq!=pitch_curfreq){ //if curfreq at next freq, but last freq != cur freq, make sure it is.
Clemo 0:cb80470434eb 152 pitch_lastfreq=pitch_curfreq;
Clemo 0:cb80470434eb 153 }
Clemo 0:cb80470434eb 154
Clemo 0:cb80470434eb 155 p = (unsigned long)pitch_curfreq; //now mult pitch by lfo, env and pitch wheel
Clemo 0:cb80470434eb 156 if (pitch_lfoamt>0){
Clemo 0:cb80470434eb 157 p = p * (Pitch_Get_LFOGain() + 1) >> PITCH_LFOBS; //mult pitch by lfo gain and bit shift. This is the fixed point maths way of mult pitch by lfo (i.e. not using floating point)
Clemo 0:cb80470434eb 158 }
Clemo 0:cb80470434eb 159 if (pitch_fenvamt>0){
Clemo 0:cb80470434eb 160 p = p * (Pitch_Get_FenvGain() + 1) >> PITCH_ENVBS; //mult pitch by env gain and bit shift. This is the fixed point maths way of mult pitch by env (i.e. not using floating point)
Clemo 0:cb80470434eb 161 }
Clemo 0:cb80470434eb 162
Clemo 0:cb80470434eb 163 if (MIDI_Get_PitchBend_Level()!=0){
Clemo 0:cb80470434eb 164 p = p * (Pitch_Get_MIDIPbendGain() + 1) >> 7UL; //mult pitch by pbend wheel and bit shift. This is the fixed point maths way of mult pitch by pbend (i.e. not using floating point)
Clemo 0:cb80470434eb 165 } //pbend is MSB only, so 0-127, hence bit shift by 7 bits
Clemo 0:cb80470434eb 166 pitch_freq_calc = p; //set pitch_freq_calc, for use in interrupt
Clemo 0:cb80470434eb 167
Clemo 0:cb80470434eb 168 if (pitch_freq_calc!=pitch_lastfreq_calc){ //see if pitch has actually changed since last calculation (to save unneccessary calc in interrupt)
Clemo 0:cb80470434eb 169 pitch_changed = true;
Clemo 0:cb80470434eb 170 pitch_lastfreq_calc = pitch_freq_calc;
Clemo 0:cb80470434eb 171 }
Clemo 0:cb80470434eb 172 else{
Clemo 0:cb80470434eb 173 pitch_changed = false;
Clemo 0:cb80470434eb 174 }
Clemo 0:cb80470434eb 175 }
Clemo 0:cb80470434eb 176
Clemo 0:cb80470434eb 177
Clemo 0:cb80470434eb 178 //Pitch LFO amount. This is stored as a byte representing knob position and a float (0-PITCH_LFOMAX)
Clemo 0:cb80470434eb 179 void Pitch_Let_LFOAmt(byte newamt)
Clemo 0:cb80470434eb 180 {
Clemo 0:cb80470434eb 181 if (newamt!=pitch_lfoamt){ //if new value different to current
Clemo 0:cb80470434eb 182 pitch_lfoamt = newamt; //set new value
Clemo 0:cb80470434eb 183 pitch_lfoamtf = (float)pitch_lfoamt * PITCH_LFOMAX / 255; //calculate new float value used to calculate LFO lookup table (0-PITCH_LFOMAX)
Clemo 0:cb80470434eb 184 memset(pitch_lfolookup,0,sizeof(pitch_lfolookup)); //clear the lookup table (quicker than loop)
Clemo 0:cb80470434eb 185 }
Clemo 0:cb80470434eb 186 }
Clemo 0:cb80470434eb 187
Clemo 0:cb80470434eb 188
Clemo 0:cb80470434eb 189 byte Pitch_Get_LFOAmt(void)
Clemo 0:cb80470434eb 190 {
Clemo 0:cb80470434eb 191 return pitch_lfoamt;
Clemo 0:cb80470434eb 192 }
Clemo 0:cb80470434eb 193
Clemo 0:cb80470434eb 194
Clemo 0:cb80470434eb 195 //Return the 'gain' of LFO. This is stored in a lookup table to save time consuming calculations each time. See the forums for further explanation of the 'gain' term
Clemo 0:cb80470434eb 196 byte Pitch_Get_LFOGain(void)
Clemo 0:cb80470434eb 197 {
Clemo 0:cb80470434eb 198 byte index;
Clemo 0:cb80470434eb 199 float f, g;
Clemo 0:cb80470434eb 200 index = LFO_Get_Level() + 127; //get the current output level of LFO (-127 - 128) and add offset (can't lookup negative array indexes)
Clemo 0:cb80470434eb 201 if (pitch_lfolookup[index]==0){ //if index of lookup table hasn't yet been calculated:
Clemo 0:cb80470434eb 202 f = (float)LFO_Get_Level() / 127; //convert LFO output to float (0 - 1)
Clemo 0:cb80470434eb 203 g = round(exp(f * pitch_lfoamtf) * PITCH_LFOMULT - 1); //calculate lookup table. e.g. lfoamt = 0: exp(0) * 64 - 1 = 63 e.g. lfoamt = 1 and lfo level = 127: exp(ln(4)) * 64 - 1 = 255 e.g. lfoamt = 1 and lfo = -127: exp(-ln(4)) * 64 - 1 = 15
Clemo 0:cb80470434eb 204 pitch_lfolookup[index] = (byte)g; //write value to lookup table
Clemo 0:cb80470434eb 205 }
Clemo 0:cb80470434eb 206 return pitch_lfolookup[index];
Clemo 0:cb80470434eb 207 }
Clemo 0:cb80470434eb 208
Clemo 0:cb80470434eb 209
Clemo 0:cb80470434eb 210 //pitch Env amount. This is stored as a byte representing knob position and a float (0-PITCH_ENVMAX)
Clemo 0:cb80470434eb 211 void Pitch_Let_FenvAmt(byte newamt)
Clemo 0:cb80470434eb 212 {
Clemo 0:cb80470434eb 213 if (newamt!=pitch_fenvamt){ //if new value different to current
Clemo 0:cb80470434eb 214 pitch_fenvamt = newamt; //set new value
Clemo 0:cb80470434eb 215 pitch_fenvamtf = (float)pitch_fenvamt * PITCH_ENVMAX / 255;//calculate new float value used to calculate env lookup table (0-PITCH_ENVMAX)
Clemo 0:cb80470434eb 216 memset(pitch_fenvlookup,0,sizeof(pitch_fenvlookup)); //clear the lookup table (quicker than loop)
Clemo 0:cb80470434eb 217 }
Clemo 0:cb80470434eb 218 }
Clemo 0:cb80470434eb 219
Clemo 0:cb80470434eb 220
Clemo 0:cb80470434eb 221 byte Pitch_Get_FenvAmt(void)
Clemo 0:cb80470434eb 222 {
Clemo 0:cb80470434eb 223 return pitch_fenvamt;
Clemo 0:cb80470434eb 224 }
Clemo 0:cb80470434eb 225
Clemo 0:cb80470434eb 226
Clemo 0:cb80470434eb 227 //Return the 'gain' of env. This is stored in a lookup table to save time consuming calculations each time. See the forums for further explanation of the 'gain' term
Clemo 0:cb80470434eb 228 byte Pitch_Get_FenvGain(void)
Clemo 0:cb80470434eb 229 {
Clemo 0:cb80470434eb 230 byte index;
Clemo 0:cb80470434eb 231 float f, g;
Clemo 0:cb80470434eb 232 index = Fenv_Get_Level() + 127; //get the current output level of env (-127 - 128) and add offset (can't lookup negative array indexes)
Clemo 0:cb80470434eb 233 if (pitch_fenvlookup[index]==0){ //if index of lookup table hasn't yet been calculated:
Clemo 0:cb80470434eb 234 f = (float)Fenv_Get_Level() / 127; //convert env output to float (0 - 1)
Clemo 0:cb80470434eb 235 g = round(exp(f * pitch_fenvamtf) * PITCH_ENVMULT - 1); //calculate lookup table. e.g. envamt = 0: exp(0) * 64 - 1 = 63 e.g. envamt = 1 and lfo level = 127: exp(ln(4)) * 64 - 1 = 255 e.g. envamt = 1 and env = -127: exp(-ln(4)) * 64 - 1 = 15
Clemo 0:cb80470434eb 236 pitch_fenvlookup[index] = (byte)g; //write value to lookup table
Clemo 0:cb80470434eb 237 }
Clemo 0:cb80470434eb 238 return pitch_fenvlookup[index];
Clemo 0:cb80470434eb 239 }
Clemo 0:cb80470434eb 240
Clemo 0:cb80470434eb 241
Clemo 0:cb80470434eb 242 //Return the 'gain' of the pitch bend. This does not require a lookup table, as it's a single value (rather than waveform or env shape)
Clemo 0:cb80470434eb 243 byte Pitch_Get_MIDIPbendGain(void)
Clemo 0:cb80470434eb 244 {
Clemo 0:cb80470434eb 245 float f, g;
Clemo 0:cb80470434eb 246 if (MIDI_Get_PitchBend_Level()!=pitch_lastpbend){ //has pitchbend value actually changed?
Clemo 0:cb80470434eb 247 f = (float)MIDI_Get_PitchBend_Level() / 63; //convert to floating point.
Clemo 0:cb80470434eb 248 g = round(exp(f * PITCH_LFOMAX) * 128 - 1); //Change 128 to change unity value (eg 64)
Clemo 0:cb80470434eb 249 pitch_pbendlookup = (byte)g; //convert to byte
Clemo 0:cb80470434eb 250 pitch_lastpbend = MIDI_Get_PitchBend_Level(); //set last pbend value
Clemo 0:cb80470434eb 251 }
Clemo 0:cb80470434eb 252 return pitch_pbendlookup;
Clemo 0:cb80470434eb 253 }
Clemo 0:cb80470434eb 254