This is a part of the Kinetiszer project.
pitch.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" //local variables unsigned long porta_starttick; //portamento - tick glide started unsigned long porta_curtick; //portamento - current tick through glide 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 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 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 float pitch_lfoamtf = 0; //lfo amount as float (0-1) float pitch_fenvamtf = 0; //env amount as float (0-1) unsigned long pitch_lastfreq_calc = 220; //last value of pitch_freq_calc char pitch_lastpbend; boolean pitch_changed = true; //lets and gets unsigned int pitch_lastfreq = 220; //last frequency set unsigned int pitch_curfreq = 220; //current frequency (must never be 0) unsigned int pitch_nextfreq = 220; //next frequency. Frequency that current frequency is heading to (based on portamento level) unsigned int pitch_portaticks = 0; //portamento speed in ticks (ticks to glide from one note to another) unsigned int pitch_portaspeed = 0; //portamento speed in ticks not taking into account of distance between prev and next notes (propotional mode) boolean pitch_propporta = false; //portamento propotional mode byte pitch_lfoamt = 0; //amount lfo effects pitch byte pitch_fenvamt = 0; //amount env effects pitch unsigned long pitch_freq_calc = 220; //master current frequency (after lfo, env, pitch wheel calculated) //lets and gets void Pitch_Let_NextFreq(unsigned int newfreq) { //set next frequency - i.e. the freq that the portamento heads towards if (newfreq!=pitch_nextfreq){ //if new value != current, then update and refresh portamento pitch_nextfreq = newfreq; pitch_lastfreq = pitch_curfreq; Pitch_RefreshPortaTicks(); } } unsigned int Pitch_Get_NextFreq(void) { return pitch_nextfreq; } void Pitch_Let_Porta(int newporta) { //set portamento speed. this is a preset value stored in hardware tab. if (newporta!=pitch_portaspeed){ pitch_portaspeed = newporta; Pitch_RefreshPortaTicks(); //if proportional mode is on, the actual porta speed (portaticks) needs calculating. } } int Pitch_Get_Porta(void) { return pitch_portaspeed; } void Pitch_ResetPorta(void) { porta_starttick = master_tick; } void Pitch_RefreshPortaTicks(void) { unsigned long p; if (pitch_portaspeed==0){ //if portamento is off, speed = 0 ticks pitch_portaticks = 0; } else if (pitch_propporta==true) { //if propotional mode is on, speeed is proportional to diff between current and next frequencies if (pitch_nextfreq>pitch_curfreq){ p = (unsigned long)(pitch_nextfreq-pitch_curfreq) * pitch_portaspeed / pitch_curfreq; } else{ p = (unsigned long)(pitch_curfreq-pitch_nextfreq) * pitch_portaspeed / pitch_nextfreq; } pitch_portaticks = (unsigned int)p; } else{ pitch_portaticks = pitch_portaspeed; //if proportional mode is off, speed is just = portaspeed } } unsigned long Pitch_Get_FreqCalc(void) { return pitch_freq_calc; //needed by interrupt loop to set frequency } boolean Pitch_Get_PitchChanged(void) { //has freq changed since last return pitch_changed; } void Pitch_Let_PropPorta(boolean newprop) { //set proportional portamento mode. refresh portaticks. if (newprop!=pitch_propporta){ pitch_propporta = newprop; Pitch_RefreshPortaTicks(); } } boolean Pitch_Get_PropPorta(void) { return pitch_propporta; } //************PITCH PROCESS**************** void Pitch_Process(void) { unsigned long p; if (pitch_curfreq!=pitch_nextfreq){ //is current frequency = next freq (i.e. the freq we want it to eventually be). first calculate the portamento if (pitch_portaticks==0){ //if portamento is off pitch_curfreq = pitch_nextfreq; //curfreq = next freq (no glide) } else{ //if portamento is on porta_curtick = master_tick - porta_starttick; //calc ticks passed if (porta_curtick>pitch_portaticks){ //curtick > portaticks, curfreq = next freq (i.e. glide has finished) pitch_curfreq = pitch_nextfreq; } else{ //otherwise 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) } } } else if (pitch_lastfreq!=pitch_curfreq){ //if curfreq at next freq, but last freq != cur freq, make sure it is. pitch_lastfreq=pitch_curfreq; } p = (unsigned long)pitch_curfreq; //now mult pitch by lfo, env and pitch wheel if (pitch_lfoamt>0){ 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) } if (pitch_fenvamt>0){ 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) } if (MIDI_Get_PitchBend_Level()!=0){ 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) } //pbend is MSB only, so 0-127, hence bit shift by 7 bits pitch_freq_calc = p; //set pitch_freq_calc, for use in interrupt if (pitch_freq_calc!=pitch_lastfreq_calc){ //see if pitch has actually changed since last calculation (to save unneccessary calc in interrupt) pitch_changed = true; pitch_lastfreq_calc = pitch_freq_calc; } else{ pitch_changed = false; } } //Pitch LFO amount. This is stored as a byte representing knob position and a float (0-PITCH_LFOMAX) void Pitch_Let_LFOAmt(byte newamt) { if (newamt!=pitch_lfoamt){ //if new value different to current pitch_lfoamt = newamt; //set new value pitch_lfoamtf = (float)pitch_lfoamt * PITCH_LFOMAX / 255; //calculate new float value used to calculate LFO lookup table (0-PITCH_LFOMAX) memset(pitch_lfolookup,0,sizeof(pitch_lfolookup)); //clear the lookup table (quicker than loop) } } byte Pitch_Get_LFOAmt(void) { return pitch_lfoamt; } //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 byte Pitch_Get_LFOGain(void) { byte index; float f, g; index = LFO_Get_Level() + 127; //get the current output level of LFO (-127 - 128) and add offset (can't lookup negative array indexes) if (pitch_lfolookup[index]==0){ //if index of lookup table hasn't yet been calculated: f = (float)LFO_Get_Level() / 127; //convert LFO output to float (0 - 1) 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 pitch_lfolookup[index] = (byte)g; //write value to lookup table } return pitch_lfolookup[index]; } //pitch Env amount. This is stored as a byte representing knob position and a float (0-PITCH_ENVMAX) void Pitch_Let_FenvAmt(byte newamt) { if (newamt!=pitch_fenvamt){ //if new value different to current pitch_fenvamt = newamt; //set new value pitch_fenvamtf = (float)pitch_fenvamt * PITCH_ENVMAX / 255;//calculate new float value used to calculate env lookup table (0-PITCH_ENVMAX) memset(pitch_fenvlookup,0,sizeof(pitch_fenvlookup)); //clear the lookup table (quicker than loop) } } byte Pitch_Get_FenvAmt(void) { return pitch_fenvamt; } //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 byte Pitch_Get_FenvGain(void) { byte index; float f, g; index = Fenv_Get_Level() + 127; //get the current output level of env (-127 - 128) and add offset (can't lookup negative array indexes) if (pitch_fenvlookup[index]==0){ //if index of lookup table hasn't yet been calculated: f = (float)Fenv_Get_Level() / 127; //convert env output to float (0 - 1) 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 pitch_fenvlookup[index] = (byte)g; //write value to lookup table } return pitch_fenvlookup[index]; } //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) byte Pitch_Get_MIDIPbendGain(void) { float f, g; if (MIDI_Get_PitchBend_Level()!=pitch_lastpbend){ //has pitchbend value actually changed? f = (float)MIDI_Get_PitchBend_Level() / 63; //convert to floating point. g = round(exp(f * PITCH_LFOMAX) * 128 - 1); //Change 128 to change unity value (eg 64) pitch_pbendlookup = (byte)g; //convert to byte pitch_lastpbend = MIDI_Get_PitchBend_Level(); //set last pbend value } return pitch_pbendlookup; }