diff options
| author | fakebitpolytechnic <fakebitpoly@gmail.com> | 2013-08-01 15:39:59 +0100 |
|---|---|---|
| committer | fakebitpolytechnic <fakebitpoly@gmail.com> | 2013-08-01 15:39:59 +0100 |
| commit | 6d2e900893f4577b94f9b3d7da7a2844d618d409 (patch) | |
| tree | 3f125ac126ed6929bfba8e277ca6594cf0636133 /CheapSynth_FM4osc2Poly_v0_5_6 | |
| parent | a1138ef1c523d3046df0ef20775519e5b7d51c47 (diff) | |
Doh, got FM poly working better via tweaked ADSR (included)
Diffstat (limited to 'CheapSynth_FM4osc2Poly_v0_5_6')
| -rw-r--r-- | CheapSynth_FM4osc2Poly_v0_5_6/ADSRslow.h | 307 | ||||
| -rw-r--r-- | CheapSynth_FM4osc2Poly_v0_5_6/CheapSynth_FM4osc2Poly_v0_5_6.ino | 51 |
2 files changed, 335 insertions, 23 deletions
diff --git a/CheapSynth_FM4osc2Poly_v0_5_6/ADSRslow.h b/CheapSynth_FM4osc2Poly_v0_5_6/ADSRslow.h new file mode 100644 index 0000000..760d136 --- /dev/null +++ b/CheapSynth_FM4osc2Poly_v0_5_6/ADSRslow.h @@ -0,0 +1,307 @@ +/* + * ADSR.h + * + * Copyright 2012 Tim Barrass. + * + * This file is part of Mozzi. + * + * Mozzi 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. + * + * Mozzi 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 Mozzi. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef ADSR_H_ +#define ADSR_H_ + +#include "Arduino.h" +//#include <util/atomic.h> +#include "Line.h" +#include "mozzi_fixmath.h" + +/** A simple ADSR envelope generator. +@todo Test whether using the template parameter makes any difference to speed, +and rationalise which units which do and don't need them. +Template objects are messy when you try to use pointers to them, +you have to include the whole template shebang in the pointer handling. +*/ +template <unsigned int CONTROL_UPDATE_RATE> +class ADSR +{ +private: + + const unsigned int AUDIO_TICKS_PER_CONTROL; + + unsigned int phase_control_step_counter; + unsigned int phase_num_control_steps; + + enum {ATTACK,DECAY,SUSTAIN,RELEASE,IDLE}; + + + struct phase{ + byte phase_type; + unsigned int control_steps; + unsigned long audio_steps; + Q8n0 level; + }attack,decay,sustain,release,idle; + + phase * current_phase; + + // Linear audio rate transitions for envelope + Line <unsigned long> transition; + + inline + unsigned int convertMsecToControlSteps(unsigned int msec){ + return (uint) (((ulong)msec*CONTROL_UPDATE_RATE)>>10); // approximate /1000 with shift + } + + inline + void setPhase(phase * next_phase) { + phase_control_step_counter = 0; + phase_num_control_steps = next_phase->control_steps; + transition.set(Q8n0_to_Q16n16(next_phase->level),next_phase-> control_steps); + current_phase = next_phase; + } + + + + inline + void checkForAndSetNextPhase(phase * next_phase) { + if (++phase_control_step_counter >= phase_num_control_steps){ + setPhase(next_phase); + } + } + + + inline + void checkForAndSetIdle() { + if (++phase_control_step_counter >= phase_num_control_steps){ + transition.set(0,0,1); + current_phase = &idle; + } + } + + + +inline + void setTime(phase * p, unsigned int msec) + { + p->control_steps=convertMsecToControlSteps(msec); + p->audio_steps = (ulong) p->control_steps * AUDIO_TICKS_PER_CONTROL; + } + + +public: + + /** Constructor. + */ + ADSR():AUDIO_TICKS_PER_CONTROL(AUDIO_RATE/CONTROL_UPDATE_RATE) + { + attack.phase_type = ATTACK; + decay.phase_type = DECAY; + sustain.phase_type = SUSTAIN; + release.phase_type = RELEASE; + idle.phase_type = IDLE; + release.level = 0; + } + + +/** Updates the internal controls of the ADSR. + Call this in updateControl(). + */ + void update(){ // control rate + + switch(current_phase->phase_type) { + + case ATTACK: + checkForAndSetNextPhase(&decay); + break; + + case DECAY: + checkForAndSetNextPhase(&sustain); + break; + + case SUSTAIN: + checkForAndSetNextPhase(&release); + break; + + case RELEASE: + checkForAndSetIdle(); + break; + + } + } + + /** Advances one audio step along the ADSR and returns the level. + Call this in updateAudio(). + @return the next value, as an unsigned int. + */ + inline + unsigned int next() + { + return Q16n16_to_Q16n0(transition.next()); + } + + + + /** Start the attack phase of the ADSR. THis will restart the ADSR no matter what phase it is up to. + */ + inline + void noteOn(){ + setPhase(&attack); + } + + + + /** Start the release phase of the ADSR. + @todo fix release for rate rather than steps (time), so it releases at the same rate whatever the current level. + */ + inline + void noteOff(){ + setPhase(&release); + } + + + + + + /** Set the attack level of the ADSR. + @param value the attack level. + */ + inline + void setAttackLevel(byte value) + { + attack.level=value; + } + + + + /** Set the decay level of the ADSR. + @param value the decay level. + */ + inline + void setDecayLevel(byte value) + { + decay.level=value; + } + + + /** Set the sustain level of the ADSR. + @param value the sustain level. Usually the same as the decay level, + for a steady sustained note. + */ + inline + void setSustainLevel(byte value) + { + sustain.level=value; + } + + /** Set the release level of the ADSR. Normally you'd make this 0, + but you have the option of some other value. + @param value the release level (normally 0). + */ + inline + void setReleaseLevel(byte value) + { + release.level=value; + } + + + + /** Set the attack and decay levels of the ADSR. This assumes a conventional + ADSR where the sustain continues at the same level as the decay, till the release ramps to 0. + @param attack the new attack level. + @param value the new sustain level. + */ + inline + void setADLevels(byte attack, byte decay) + { + setAttackLevel(attack); + setDecayLevel(decay); + setSustainLevel(decay); + setReleaseLevel(0); + } + + + + + + + + /** Set the attack time of the ADSR in milliseconds. + The actual time taken will be resolved within the resolution of CONTROL_RATE. + @param value the unsigned int attack time in milliseconds. + */ + inline + void setAttackTime(unsigned int msec) + { + setTime(&attack, msec); + } + + + /** Set the decay time of the ADSR in milliseconds. + The actual time taken will be resolved within the resolution of CONTROL_RATE. + @param value the unsigned int decay time in milliseconds. + */ + inline + void setDecayTime(unsigned int msec) + { + setTime(&decay, msec); + } + + + /** Set the sustain time of the ADSR in milliseconds. + The actual time taken will be resolved within the resolution of CONTROL_RATE. + The sustain phase will finish if the ADSR recieves a noteOff(). + @param value the unsigned int sustain time in milliseconds. + */ + inline + void setSustainTime(unsigned int msec) + { + setTime(&sustain, msec); + } + + + + /** Set the release time of the ADSR in milliseconds. + The actual time taken will be resolved within the resolution of CONTROL_RATE. + @param value the unsigned int release time in milliseconds. + */ + inline + void setReleaseTime(unsigned int msec) + { + setTime(&release, msec); + } + + + + /** Set the attack, decay and release times of the ADSR in milliseconds. + The actual times will be resolved within the resolution of CONTROL_RATE. + @param attack_ms the new attack time in milliseconds. + @param decay_ms the new decay time in milliseconds. + @param decay_ms the new sustain time in milliseconds. + @param release_ms the new release time in milliseconds. + */ + inline + void setTimes(unsigned int attack_ms, unsigned int decay_ms, unsigned int sustain_ms, unsigned int release_ms) + { + setAttackTime(attack_ms); + setDecayTime(decay_ms); + setSustainTime(sustain_ms); + setReleaseTime(release_ms); + } + + +}; + +#endif /* ADSR_H_ */ + diff --git a/CheapSynth_FM4osc2Poly_v0_5_6/CheapSynth_FM4osc2Poly_v0_5_6.ino b/CheapSynth_FM4osc2Poly_v0_5_6/CheapSynth_FM4osc2Poly_v0_5_6.ino index 5c776b0..e5b705b 100644 --- a/CheapSynth_FM4osc2Poly_v0_5_6/CheapSynth_FM4osc2Poly_v0_5_6.ino +++ b/CheapSynth_FM4osc2Poly_v0_5_6/CheapSynth_FM4osc2Poly_v0_5_6.ino @@ -35,14 +35,16 @@ detuning on pitch bend strip, massive upwards zoom on both notes when no portame Mode 4 - intentionally left blank so you know where you are Standard controls: -Note decay length on forward arrow to the right of big transparent Mode button (currently applies -to less computationally demanding envelopes in Mode 0 and 2), toggles between short/medium/long Different modulation freq harmonics on the right-hand corner up and down keys between octave up and octave down (all modes except Mode 2). Portamento speed on Back arrow to the left of big transparent Mode button, toggles between no portamento (ie 2-note polyphony in modes that support it), 10ms fast glide between notes, 400ms slow glide. + +[updated: this note decay now doesn't do anything as I've moved everything to "slow" ADSR..!] +Note decay length on forward arrow to the right of big transparent Mode button (currently applies +to less computationally demanding envelopes in Mode 0 and 2), toggles between short/medium/long */ #include <MIDI.h> @@ -53,7 +55,7 @@ Portamento speed on Back arrow to the left of big transparent Mode button, toggl #include <tables/saw1024_int8.h> // table for Oscils to play #include <tables/smoothsquare8192_int8.h> // NB portamento requires table > 512 size? #include <mozzi_midi.h> -#include <ADSR.h> +#include <ADSRslow.h> #include <mozzi_fixmath.h> #include <LowPassFilter.h> #include <Portamento.h> @@ -76,7 +78,7 @@ Oscil<SAW1024_NUM_CELLS, AUDIO_RATE> oscSaw2(SAW1024_DATA); // envelope generator ADSR <CONTROL_RATE> envelope; ADSR <CONTROL_RATE> envelope2; -Ead kEnvelopeGuitar1(CONTROL_RATE); +Ead kEnvelopeGuitar1(CONTROL_RATE); //left in here for future use but currently not vital? Ead kEnvelopeGuitar2(CONTROL_RATE); Portamento <CONTROL_RATE>aPortamento; @@ -87,8 +89,8 @@ Portamento <CONTROL_RATE>aPortamento; long vibrato=0; long vibrato2=0; -int gainGuitar1; -int gainGuitar2; +int chanGain1; +int chanGain2; int LFO; unsigned int carrierXmodDepth1; unsigned int carrierXmodDepth2; @@ -136,9 +138,9 @@ void setup() { MIDI.setHandleStart(HandleStart); envelope.setADLevels(127,100); - envelope.setTimes(20,20,20000,850); // 20000 is so the note will sustain 20 seconds unless a noteOff comes + envelope.setTimes(20,20,20000,1200); // 20000 is so the note will sustain 20 seconds unless a noteOff comes envelope2.setADLevels(127,100); - envelope2.setTimes(20,20,20000,850); // 20000 is so the note will sustain 20 seconds unless a noteOff comes + envelope2.setTimes(20,20,20000,1200); // 20000 is so the note will sustain 20 seconds unless a noteOff comes kEnvelopeGuitar1.set(20, 4000); kEnvelopeGuitar2.set(20, 4000); @@ -214,7 +216,7 @@ void HandleStop () { //this is the Back button on the Xbox keyboard aPortamento.setTime( (portSpeed*portSpeed*100) ); // values of 0,100,400 milliseconds? (next 900) } -void HandleStart () { //this is the "Start" button on the Xbox keyboard +void HandleStart () { //CURRENTLY NOT USED noteLength=(noteLength+1)%3; // loop from 0-2 kEnvelopeGuitar1.set(20, (noteLength+1)*(noteLength+1)*(noteLength+1)*500 ); kEnvelopeGuitar2.set(20, (noteLength+1)*(noteLength+1)*(noteLength+1)*500 ); @@ -232,12 +234,12 @@ void HandleNoteOff(byte channel, byte note, byte velocity) { if ( (note == lastnote1) ) //kill envelope if prev note released { envelope.noteOff(); - if (gainGuitar2>0) {nextenv=1;} + if (chanGain2) {nextenv=1;} } if ( (note == lastnote2) ) {envelope2.noteOff(); - if (gainGuitar1>0) {nextenv=2;} + if (chanGain1) {nextenv=2;} } } @@ -295,10 +297,13 @@ void updateControl(){ envelope.update(); envelope2.update(); - gainGuitar1 = kEnvelopeGuitar1.next(); - gainGuitar2 = kEnvelopeGuitar2.next(); - if(gainGuitar1 > 120) gainGuitar1 = 120; - if(gainGuitar2 > 120) gainGuitar2 = 120; + chanGain1 = envelope.next(); + chanGain2 = envelope2.next(); + +// chanGain1 = kEnvelopeGuitar1.next(); +// chanGain2 = kEnvelopeGuitar2.next(); + if(chanGain1 > 120) chanGain1 = 120; + if(chanGain2 > 120) chanGain2 = 120; if ( portSpeed ) { @@ -335,8 +340,8 @@ vibrato*= carrierXmodDepth1; vibrato2 = ( LFO * oscModulator2.next() ) >>7 ; vibrato2*= carrierXmodDepth2; return (int) ( - (oscCarrier.phMod( vibrato >>3 ) * gainGuitar1 ) - + (oscSquare2.phMod( vibrato2>>3 ) * gainGuitar2 ) + (oscCarrier.phMod( vibrato >>3 ) * chanGain1 ) + + (oscSquare2.phMod( vibrato2>>3 ) * chanGain2 ) ) >> 9; // >> 9 = 9 fast binary divisions by 2 = divide by 512 break; @@ -347,22 +352,22 @@ squaredsin1= ( (oscModulator.next() & bitmask) ) <<multiplier; squaredsin2= ( (oscModulator2.next() & bitmask) ) <<multiplier; return (int) ( - ( ( (oscCarrier.next() + squaredsin1 )>>1 )*envelope.next() ) -+ ( ( (oscSquare2.next() + squaredsin2 )>>1 )*envelope2.next() ) + ( ( (oscCarrier.next() + squaredsin1 )>>1 )* chanGain1 ) ++ ( ( (oscSquare2.next() + squaredsin2 )>>1 )* chanGain2 ) ) >> 8; break; case 2: // 2 tri poly return (int)( - ( oscSaw1.next() *gainGuitar1 ) - + ( oscSaw2.next() *gainGuitar2 ) + ( oscSaw1.next() *chanGain1 ) + + ( oscSaw2.next() *chanGain2 ) ) >> 9; break; case 3: // STARTED AS 2 sq poly XORd with respective sines, now big whoop return (int)( - ( (oscCarrier.next()^oscModulator.next()) * envelope.next() ) - + ( (oscSquare2.next()^oscModulator2.next()) * envelope2.next() ) + ( (oscCarrier.next()^oscModulator.next()) * chanGain1 ) + + ( (oscSquare2.next()^oscModulator2.next())* chanGain2 ) ) >> 8; //return (int) ( (oscCarrier.next() | ( (oscModulator.next() * int (modDepth+1))>>3 ) ) * (envelope.next() ) ) >> 7; break; |
