+ EQ-5: refactoring to eliminate code duplication, side effect: stereo meters
[calf.git] / src / calf / synth.h
blobdc5c8ebae382368c4c2cfb3acf5cc2de2a676db9
1 /* Calf DSP Library
2 * Framework for synthesizer-like plugins. This is based
3 * on my earlier work on Drawbar electric organ emulator.
5 * Copyright (C) 2007 Krzysztof Foltman
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General
18 * Public License along with this program; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02111-1307, USA.
22 #ifndef __CALF_SYNTH_H
23 #define __CALF_SYNTH_H
25 #include <list>
26 #include <stack>
27 #include <bitset>
28 #include "primitives.h"
29 #include "audio_fx.h"
31 namespace dsp {
33 /**
34 * A kind of set with fast non-ordered iteration, used for storing lists of pressed keys.
36 class keystack {
37 private:
38 int dcount;
39 uint8_t active[128];
40 uint8_t states[128];
41 public:
42 keystack() {
43 memset(states, 0xFF, sizeof(states));
44 dcount = 0;
46 void clear() {
47 for (int i=0; i<dcount; i++)
48 states[active[i]] = 0xFF;
49 dcount = 0;
51 bool push(int key) {
52 assert(key >= 0 && key <= 127);
53 if (states[key] != 0xFF) {
54 return true;
56 states[key] = dcount;
57 active[dcount++] = key;
58 return false;
60 bool pop(int key) {
61 if (states[key] == 0xFF)
62 return false;
63 int pos = states[key];
64 if (pos != dcount-1) {
65 // reuse the popped item's stack position for stack top
66 int last = active[dcount-1];
67 active[pos] = last;
68 // mark that position's new place on stack
69 states[last] = pos;
71 states[key] = 0xFF;
72 dcount--;
73 return true;
75 inline bool has(int key) {
76 return states[key] != 0xFF;
78 inline int count() {
79 return dcount;
81 inline bool empty() {
82 return (dcount == 0);
84 inline int nth(int n) {
85 return active[n];
89 /**
90 * Convert MIDI note number to normalized UINT phase (where 1<<32 is full cycle).
91 * @param MIDI note number
92 * @param cents detune in cents (1/100 of a semitone)
93 * @param sr sample rate
95 inline unsigned int midi_note_to_phase(int note, double cents, int sr) {
96 double incphase = 440*pow(2.0, (note-69)/12.0 + cents/1200.0)/sr;
97 if (incphase >= 1.0) incphase = fmod(incphase, 1.0);
98 incphase *= 65536.0*65536.0;
99 return (unsigned int)incphase;
102 // Base class for all voice objects
103 class voice {
104 public:
105 int sample_rate;
106 bool released, sostenuto, stolen;
108 voice() : sample_rate(-1), released(false), sostenuto(false), stolen(false) {}
110 /// reset voice to default state (used when a voice is to be reused)
111 virtual void setup(int sr) { sample_rate = sr; }
112 /// reset voice to default state (used when a voice is to be reused)
113 virtual void reset()=0;
114 /// a note was pressed
115 virtual void note_on(int note, int vel)=0;
116 /// a note was released
117 virtual void note_off(int vel)=0;
118 /// check if voice can be removed from active voice list
119 virtual bool get_active()=0;
120 /// render voice data to buffer
121 virtual void render_to(float (*buf)[2], int nsamples)=0;
122 /// very fast note off
123 virtual void steal()=0;
124 /// return the note used by this voice
125 virtual int get_current_note()=0;
126 virtual float get_priority() { return stolen ? 20000 : (released ? 1 : (sostenuto ? 200 : 100)); }
127 /// empty virtual destructor
128 virtual ~voice() {}
131 /// An "optimized" voice class using fixed-size processing units
132 /// and fixed number of channels. The drawback is that voice
133 /// control is not sample-accurate, and no modulation input
134 /// is possible, but it should be good enough for most cases
135 /// (like Calf Organ).
136 template<class Base>
137 class block_voice: public Base {
138 public:
139 // derived from Base
140 // enum { Channels = 2 };
141 using Base::Channels;
142 // enum { BlockSize = 16 };
143 using Base::BlockSize;
144 // float output_buffer[BlockSize][Channels];
145 using Base::output_buffer;
146 // void render_block();
147 using Base::render_block;
148 unsigned int read_ptr;
150 block_voice()
152 read_ptr = BlockSize;
154 virtual void reset()
156 Base::reset();
157 read_ptr = BlockSize;
159 virtual void render_to(float (*buf)[2], int nsamples)
161 int p = 0;
162 while(p < nsamples)
164 if (read_ptr == BlockSize)
166 render_block();
167 read_ptr = 0;
169 int ncopy = std::min<int>(BlockSize - read_ptr, nsamples - p);
170 for (int i = 0; i < ncopy; i++)
171 for (int c = 0; c < Channels; c++)
172 buf[p + i][c] += output_buffer[read_ptr + i][c];
173 p += ncopy;
174 read_ptr += ncopy;
179 /// Base class for all kinds of polyphonic instruments, provides
180 /// somewhat reasonable voice management, pedal support - and
181 /// little else. It's implemented as a base class with virtual
182 /// functions, so there's some performance loss, but it shouldn't
183 /// be horrible.
184 /// @todo it would make sense to support all notes off controller too
185 struct basic_synth {
186 protected:
187 /// Current sample rate
188 int sample_rate;
189 /// Hold pedal state
190 bool hold;
191 /// Sostenuto pedal state
192 bool sostenuto;
193 /// Voices currently playing
194 std::list<dsp::voice *> active_voices;
195 /// Voices allocated, but not used
196 std::stack<dsp::voice *> unused_voices;
197 /// Gate values for all 128 MIDI notes
198 std::bitset<128> gate;
199 /// Maximum allocated number of channels
200 unsigned int polyphony_limit;
202 void kill_note(int note, int vel, bool just_one);
203 public:
204 virtual void setup(int sr) {
205 sample_rate = sr;
206 hold = false;
207 sostenuto = false;
208 polyphony_limit = (unsigned)-1;
210 virtual void trim_voices();
211 virtual dsp::voice *give_voice();
212 virtual dsp::voice *alloc_voice()=0;
213 virtual dsp::voice *steal_voice();
214 virtual void render_to(float (*output)[2], int nsamples);
215 virtual void note_on(int note, int vel);
216 virtual void percussion_note_on(int note, int vel) {}
217 virtual void control_change(int ctl, int val);
218 virtual void note_off(int note, int vel);
219 /// amt = -8192 to 8191
220 virtual void pitch_bend(int amt) {}
221 virtual void on_pedal_release();
222 virtual bool check_percussion() { return active_voices.empty(); }
223 virtual ~basic_synth();
228 #endif