summaryrefslogtreecommitdiff
path: root/synth/channel.cpp
diff options
context:
space:
mode:
authorFlorian Jung <flo@thinkpad.(none)>2010-12-29 16:55:25 +0100
committerFlorian Jung <flo@thinkpad.(none)>2010-12-29 16:55:25 +0100
commit7113f02ae87482211aec5046f9ac46c3cc9ad017 (patch)
treeb6484b45317e7e80567d9902cf94843d227ce30e /synth/channel.cpp
Initial commit
Diffstat (limited to 'synth/channel.cpp')
-rw-r--r--synth/channel.cpp338
1 files changed, 338 insertions, 0 deletions
diff --git a/synth/channel.cpp b/synth/channel.cpp
new file mode 100644
index 0000000..35ee375
--- /dev/null
+++ b/synth/channel.cpp
@@ -0,0 +1,338 @@
+#include "channel.h"
+
+#include "math.h"
+#include "globals.h"
+
+Channel::Channel()
+{
+ volume=ONE;
+ set_program(0);
+ curr_prg.controller[NO_CONT]=1;
+ quick_release=0;
+ always_reattack=false;
+ portamento_frames2=portamento_frames=0;
+ do_portamento=false;
+ pitchbend=ONE;
+ n_voices=0;
+
+ max_pitchbend=1.0;
+
+ set_balance(64);
+}
+
+Channel::~Channel()
+{
+ panic(); //deletes all notes and empties notes-list
+}
+
+void Channel::cleanup()
+{
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end(); it++)
+ if ((*it)->still_active()==false)
+ {
+ delete *it;
+ it=notes.erase(it);
+ }
+}
+
+fixed_t Channel::get_sample()
+{
+ fixed_t sum=0;
+
+ for (list<Note*>::iterator it=notes.begin(); it!=notes.end(); it++)
+ sum+=(*it)->get_sample();
+
+ return sum*volume >>SCALE;
+}
+
+void Channel::event(uint8_t a, uint8_t b, uint8_t c)
+{
+ switch(a & 0xF0)
+ {
+ case 0x80: note_off(b); break;
+ case 0x90: note_on(b,c); break;
+ case 0xA0: break; //IMPLEMENTME: polyphonic aftertouch (note, dynamic)
+ case 0xB0: set_controller(b,c); break;
+ case 0xC0: set_program(b); break;
+ case 0xD0: break; //IMPLEMENTME: monotonic aftertouch (dynamic)
+ case 0xE0: set_pitch_bend( ( (((b&0x7F) + ((c&0x7F)<<7)) - 8192) / 8192.0 ) * max_pitchbend ); break;
+ case 0xF0: break; //own controls/sysex (to be implemented) IMPLEMENTME
+ default: output_verbose("NOTE: got unknown command "+ IntToStrHex(a&0xF0) +", ignoring it\n");
+ ;
+ }
+}
+
+void Channel::note_off(int note)
+{
+ note_on(note,0);
+}
+
+void Channel::note_on(int note, int vel)
+{
+ list<Note*>::iterator it;
+ if (vel>0) //note on
+ {
+ if ( (n_voices==1) && (!notes.empty()) )
+ {
+ //no need to create a new note; reuse the existing
+ Note *n; //i'm lazy
+ n= *(notes.begin());
+
+ if (n->get_program() != program)
+ {
+ //if the program has changed, kill the previous note and
+ //create a new one
+ delete n;
+ notes.clear();
+
+ notes.push_back( new Note(note,(float)vel/128.0,
+ curr_prg,
+ portamento_frames,
+ pitchbend,
+ program) );
+
+ }
+ else //program did not change
+ {
+ //if not still active, don't do portamento
+ n->set_note(note,n->still_active());
+ n->set_vel((float)vel/128.0);
+ if (always_reattack || !n->still_active()) n->reattack();
+ //no need to push back. would become #1 instead of #1
+ }
+ }
+ else
+ {
+ bool neednewnote=true;
+ if (always_reattack)
+ {
+ for (it=notes.begin(); it!=notes.end(); it++)
+ if ( ((*it)->get_note()==note) && ((*it)->get_program()==program) )
+ {
+ neednewnote=false;
+ (*it)->reattack();
+ (*it)->set_vel((float)vel/128.0);
+ notes.push_back(*it); //reorder notes
+ notes.erase(it);
+ break;
+ }
+ }
+ if (neednewnote)
+ notes.push_back( new Note(note,(float)vel/128.0,
+ curr_prg,
+ portamento_frames,
+ pitchbend,
+ program) );
+ apply_voice_limit();
+ }
+ }
+ else //note off
+ {
+ for (it=notes.begin(); it!=notes.end(); it++)
+ if ((*it)->get_note()==note)
+ (*it)->release();
+ }
+
+}
+
+void Channel::set_n_voices(int val)
+{
+ n_voices=val;
+
+ if ((n_voices<=0) || (n_voices>=128))
+ n_voices=0; //unlimited
+
+ apply_voice_limit();
+}
+
+void Channel::apply_voice_limit()
+{
+ if (n_voices) //is a limit defined?
+ {
+ int diff=notes.size()-n_voices;
+ if (diff>0)
+ {
+ list<Note*>::iterator it=notes.begin();
+
+ if (quick_release)
+ for (int i=0;i<diff;i++)
+ {
+ (*it)->release_quickly(quick_release);
+ it++;
+ }
+ else
+ for (int i=0;i<diff;i++)
+ {
+ delete (*it);
+ it=notes.erase(it);
+ }
+ }
+ }
+}
+
+
+void Channel::set_controller(int con,int val)
+{
+ switch (con)
+ {
+ case 3: always_reattack=(val>=64);
+ case 5: set_portamento_time(val); break;
+ case 7: set_volume(val); break;
+ case 8: set_balance(val); break;
+ case 65: set_portamento(val); break;
+ case 119: set_quick_release(val);
+ case 120: panic(); break;
+ case 121: reset_controllers(); break;
+ case 123: release_all(); break;
+ case 126: set_n_voices(val); break;
+ case 127: set_n_voices(999); break;
+ default: set_user_controller(con,val); break;
+ }
+}
+
+void Channel::set_user_controller(int con, int val)
+{
+ curr_prg.controller[con]=val;
+ for (set<parameter_t>::iterator it=curr_prg.controller_affects[con].begin(); it!=curr_prg.controller_affects[con].end(); it++)
+ recalc_param(*it,curr_prg);
+}
+
+void Channel::recalc_param(const parameter_t &par, program_t &prg)
+{
+ fixed_t val=0;
+
+ list<term_t> *l;
+ l=&(prg.formula[par]);
+
+ for (list<term_t>::iterator it=l->begin(); it!=l->end(); it++)
+ val+=curr_prg.controller[it->c]*it->f;
+
+ if (val<0) val=0;
+
+ // now we have the final value of the formula in units of fixed_t
+ // in the range 0..+infinity
+
+ switch(par.par)
+ {
+ case SUSTAIN:
+ case FILTER_SUSTAIN: if (val>ONE) val=ONE; break;
+
+ case TREM_LFO:
+ case VIB_LFO:
+ case FILTER_TREM_LFO: val=val>>SCALE; if (val>=N_LFOS+1) val=N_LFOS+1 -1; break;
+
+ case TREMOLO:
+ case VIBRATO:
+ case FILTER_TREMOLO: val=val>>SCALE; if (val>=N_LFO_LEVELS) val=N_LFO_LEVELS-1; break;
+
+ case WAVEFORM: val=val>>SCALE; if (val>=N_WAVEFORMS) val=N_WAVEFORMS-1; break;
+
+ case FILTER_RESONANCE: if (val>ONE) val=ONE; break;
+
+ default: break;
+ }
+
+ // now we have the value clipped to the valid range. for stuff
+ // expecting real numbers, it's in units of fixed_t. for booleans
+ // it's zero or nonzero. for stuff expecting integers, like lfo,
+ // waveform etc it's in int (i.e., val/ONE is very small, while
+ // val is what we want)
+
+ for (list<Note*>::iterator it=notes.begin(); it!=notes.end(); it++)
+ (*it)->set_param(par, val);
+
+ curr_prg.set_param(par, val);
+}
+
+void Channel::reset_controllers()
+{
+ program_t *orig=&program_settings[program];
+
+ for (int i=0;i<128;i++)
+ set_user_controller(i,orig->controller[i]);
+}
+
+void Channel::set_quick_release(int val)
+//ranges from zero to one second.
+{
+ quick_release=samp_rate*val/128;
+}
+
+void Channel::set_volume(int val)
+{
+ volume=val*ONE/128;
+}
+
+void Channel::set_balance(int val)
+{
+#ifdef STEREO
+ balR=val/64.0;
+ balL=(128-val)/64.0;
+#endif
+}
+
+void Channel::set_portamento(int val)
+{
+ if (val>=64)
+ {
+ do_portamento=true;
+ set_real_portamento_frames();
+ }
+ else
+ {
+ do_portamento=false;
+ set_real_portamento_frames();
+ }
+}
+
+void Channel::set_portamento_time(int val)
+{
+ portamento_frames2=samp_rate*val*max_port_time_sec/128;
+ if (do_portamento)
+ set_real_portamento_frames();
+}
+
+void Channel::set_real_portamento_frames()
+{
+ if (do_portamento)
+ portamento_frames=portamento_frames2;
+ else
+ portamento_frames=0;
+
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end(); it++)
+ (*it)->set_portamento_frames(portamento_frames);
+}
+
+void Channel::panic()
+{
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end();)
+ {
+ delete *it;
+ it=notes.erase(it);
+ }
+}
+
+void Channel::release_all()
+{
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end(); it++)
+ (*it)->release();
+}
+
+void Channel::set_program(int prog)
+{
+ program=prog;
+ curr_prg=program_settings[program];
+}
+
+void Channel::set_pitch_bend(float val)
+{
+ pitchbend=pow(2.0,val/12.0)*ONE;
+
+ list<Note*>::iterator it;
+ for (it=notes.begin(); it!=notes.end(); it++)
+ (*it)->set_pitchbend(pitchbend);
+}