arduino-audio-tools
Synthesizer.h
1 #pragma once
2 
3 #include "AudioTools/CoreAudio/AudioLogger.h"
4 #include "AudioTools/CoreAudio/AudioTypes.h"
5 #include "AudioTools/CoreAudio/AudioBasic/Collections.h"
6 #include "AudioTools/CoreAudio/AudioActions.h"
7 #include "AudioTools/CoreAudio/AudioStreams.h"
8 #include "SoundGenerator.h"
9 #include "AudioEffects.h"
10 #ifdef USE_MIDI
11 #include "Midi.h"
12 #endif
13 
14 namespace audio_tools {
15 
23  public:
24  virtual ~AbstractSynthesizerChannel() = default;
25  virtual AbstractSynthesizerChannel* clone() = 0;
27  virtual void begin(AudioInfo config);
29  virtual bool isActive() = 0;
31  virtual void keyOn(int nte, float tgt) = 0;
33  virtual void keyOff() = 0;
35  virtual int16_t readSample()=0;
37  virtual int note() = 0 ;
38 };
39 
48  public:
51 
53  setGenerator(generator);
54  }
55 
58 
59  DefaultSynthesizerChannel *clone() override {
60  TRACED();
61  auto result = new DefaultSynthesizerChannel(*this);
62  result->begin(config);
63  return result;
64  }
65 
66  void setGenerator(SoundGenerator<int16_t> &generator){
67  p_generator = &generator;
68  }
69 
70  virtual void begin(AudioInfo config) override {
71  TRACEI();
72  this->config = config;
73  config.logInfo();
74 
75  // setup generator
76  if (p_generator==nullptr){
77  static FastSineGenerator<int16_t> sine;
78  p_generator = &sine;
79  }
80  p_generator->begin(config);
81 
82  // find ADSRGain
83  p_adsr = (ADSRGain*) effects.findEffect(1);
84  if (p_adsr==nullptr){
85  p_adsr = new ADSRGain(0.0001, 0.0001, 0.8, 0.0005);
86  p_adsr->setId(1);
87  effects.addEffect(p_adsr);
88  }
89  }
90 
91  virtual bool isActive() override{
92  return p_adsr != nullptr ? p_adsr->isActive() : false;
93  }
94 
96  virtual void keyOn(int note, float tgt) override{
97  if (p_generator!=nullptr){
98  p_generator->setFrequency(note);
99  }
100  if (p_adsr!=nullptr){
101  actual_note = note;
102  p_adsr->keyOn(tgt);
103  }
104  }
105 
106  virtual void keyOff() override{
107  TRACED();
108  if (p_adsr!=nullptr){
109  p_adsr->keyOff();
110  } else {
111  LOGE("p_adsr is null")
112  }
113  }
114 
115  virtual int16_t readSample() override{
116  if (p_generator==nullptr) return 0;
117  int16_t sample = p_generator->readSample();
118  int size = effects.size();
119  for (int j=0; j<size; j++){
120  sample = effects[j]->process(sample);
121  }
122  return sample;
123  }
124 
125  virtual int note() override {
126  return actual_note;
127  }
128 
129  void addEffect(AudioEffect *ptr){
130  effects.addEffect(ptr);
131  }
132 
133  protected:
134  AudioInfo config;
135  AudioEffectCommon effects;
136  SoundGenerator<int16_t> *p_generator = nullptr;
137  ADSRGain *p_adsr = nullptr;
138  int actual_note = 0;
139 
140 };
141 
142 
150  int pin;
151  float note;
152 };
153 
161 class Synthesizer : public SoundGenerator<int16_t> {
162  public:
163  Synthesizer() {
164  defaultChannel = new DefaultSynthesizerChannel();
165  }
166 
168  defaultChannel = ch;
169  }
170 
171  Synthesizer(Synthesizer const&) = delete;
172  Synthesizer& operator=(Synthesizer const&) = delete;
173 
174  virtual ~Synthesizer(){
175  TRACED();
176  for (int j=0;j<channels.size();j++){
177  delete channels[j];
178  channels[j] = nullptr;
179  }
180 
181 #ifdef USE_MIDI
182  delete p_synth_action;
183 #endif
184  }
185 
186  bool begin(AudioInfo config) {
187  TRACEI();
188  this->cfg = config;
190  // provide config to defaut
191  defaultChannel->begin(config);
192  return true;
193  }
194 
195  void keyOn(int note, float tgt=0){
196  LOGI("keyOn: %d", note);
197  AbstractSynthesizerChannel *channel = getFreeChannel();
198  if (channel!=nullptr){
199  channel->keyOn(note, tgt);
200  } else {
201  LOGW("No channel available for %d",note);
202  }
203  }
204 
205  void keyOff(int note){
206  LOGI("keyOff: %d", note);
207  AbstractSynthesizerChannel *channel = getNoteChannel(note);
208  if (channel!=nullptr){
209  channel->keyOff();
210  }
211  }
212 
214  int16_t readSample() override {
215  int total = 0;
216  uint16_t count = 0;
217  // calculate sum of all channels
218  for (int j=0;j<channels.size();j++){
219  if (channels[j]->isActive()){
220  count++;
221  total += channels[j]->readSample();
222  }
223  }
224  // prevent divide by zero
225  int result = 0;
226  if (count>0){
227  result = NumberConverter::clipT<int16_t>(total / count);
228  }
229  return result;
230  }
231 
233  void setKeys(AudioActions &actions, SynthesizerKey* p_keys, AudioActions::ActiveLogic activeValue){
234  while (p_keys->note > 0){
235  actions.add(p_keys->pin, callbackKeyOn, callbackKeyOff, activeValue , new KeyParameter(this, p_keys->note));
236  p_keys++;
237  }
238  }
239 
241  void setMidiName(const char* name){
242  midi_name = name;
243  }
244 
245  protected:
246  AudioInfo cfg;
247  AbstractSynthesizerChannel* defaultChannel;
249  const char* midi_name = "Synthesizer";
250 
251  struct KeyParameter {
252  KeyParameter(Synthesizer* synth, int nte){
253  p_synthesizer=synth;
254  note = nte;
255  };
256  Synthesizer *p_synthesizer = nullptr;
257  int note;
258  };
259 
260 #ifdef USE_MIDI
261 
263  class SynthAction : public MidiAction {
264  private:
265  Synthesizer *synth=nullptr;
266  public:
267  SynthAction(Synthesizer *synth){
268  this->synth = synth;
269  }
270  void onNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) {
271  int frq = MidiCommon::noteToFrequency(note);
272  float vel = 1.0/127.0 * velocity;
273  synth->keyOn(frq, vel);
274  }
275  void onNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) {
276  int frq = MidiCommon::noteToFrequency(note);
277  synth->keyOff(frq);
278  }
279  void onControlChange(uint8_t channel, uint8_t controller, uint8_t value) {}
280  void onPitchBend(uint8_t channel, uint8_t value) {}
281  };
282 
283  SynthAction *p_synth_action = new SynthAction(this);
284  // Midi BLE Server
285  MidiBleServer ble = MidiBleServer(midi_name, p_synth_action);
286 
287 #endif
288 
289  // gets the channel for the indicated note
290  AbstractSynthesizerChannel *getNoteChannel(int note){
291  LOGI("getNoteChannel: %d", note);
292  for (int j=0;j<channels.size();j++){
293  if (channels[j]->note() == note){
294  return channels[j];
295  }
296  }
297  return nullptr;
298  }
299 
300  // gets a free channel
301  AbstractSynthesizerChannel *getFreeChannel(){
302  LOGI("getFreeChannel");
303  for (int j=0;j<channels.size();j++){
304  if (!channels[j]->isActive()){
305  return channels[j];
306  }
307  }
308  LOGI("No free channel found: Adding new channel");
309  AbstractSynthesizerChannel* ch = defaultChannel->clone();
310  channels.push_back(ch);
311  return ch;
312  }
313 
314  static void callbackKeyOn(bool active, int pin, void* ref){
315  TRACEI();
316  KeyParameter* par = (KeyParameter*)ref;
317  if (par !=nullptr && par->p_synthesizer!=nullptr){
318  par->p_synthesizer->keyOn(par->note);
319  } else {
320  LOGE("callbackKeyOn: unexpected null")
321  }
322  }
323 
324  static void callbackKeyOff(bool active, int pin, void* ref){
325  TRACEI();
326  KeyParameter* par = (KeyParameter*)ref;
327  if (par !=nullptr){
328  if (par->p_synthesizer!=nullptr){
329  par->p_synthesizer->keyOff(par->note);
330  } else {
331  LOGE("callbackKeyOff: p_synthesizer is null");
332  }
333  } else {
334  LOGE("callbackKeyOff: ref is null");
335  }
336  }
337 };
338 
339 } // namespace
340 
ADSR Envelope: Attack, Decay, Sustain and Release. Attack is the time taken for initial run-up oeffec...
Definition: AudioEffect.h:357
Defines the sound generation for one channel. A channel is used to process an indivudual key so that ...
Definition: Synthesizer.h:22
virtual void keyOff()=0
Provides the key off event to ADSR to stop the sound.
virtual void begin(AudioInfo config)
Start the sound generation.
virtual bool isActive()=0
Checks if the ADSR is still active - and generating sound.
virtual int16_t readSample()=0
Provides the next sample.
virtual int note()=0
Provides the actual midi note that is played.
virtual void keyOn(int nte, float tgt)=0
Provides the key on event to ADSR to start the sound.
A simple class to assign functions to gpio pins e.g. to implement a simple navigation control or volu...
Definition: AudioActions.h:29
void add(Action &action)
Adds an Action.
Definition: AudioActions.h:131
size_t size()
Provides the actual number of defined effects.
Definition: AudioEffects.h:45
void addEffect(AudioEffect &effect)
Adds an effect object (by reference)
Definition: AudioEffects.h:27
AudioEffect * findEffect(int id)
Finds an effect by id.
Definition: AudioEffects.h:50
Abstract Base class for Sound Effects.
Definition: AudioEffect.h:21
void setId(int id)
Allows to identify an effect.
Definition: AudioEffect.h:41
Default implementation for a Channel. You can provide the Sound Generator as parameter to the effects...
Definition: Synthesizer.h:47
DefaultSynthesizerChannel()=default
Default constructor.
virtual void keyOn(int note, float tgt) override
start to play a note - note expects the frequency of the note!
Definition: Synthesizer.h:96
virtual void keyOff() override
Provides the key off event to ADSR to stop the sound.
Definition: Synthesizer.h:106
virtual void begin(AudioInfo config) override
Start the sound generation.
Definition: Synthesizer.h:70
virtual int note() override
Provides the actual midi note that is played.
Definition: Synthesizer.h:125
virtual int16_t readSample() override
Provides the next sample.
Definition: Synthesizer.h:115
virtual bool isActive() override
Checks if the ADSR is still active - and generating sound.
Definition: Synthesizer.h:91
DefaultSynthesizerChannel(DefaultSynthesizerChannel &ch)=default
Copy constructor.
virtual T readSample()=0
Provides a single sample.
virtual void setFrequency(float frequency)
Abstract method: not implemented! Just provides an error message...
Definition: SoundGenerator.h:82
virtual bool isActive()
Definition: SoundGenerator.h:55
A simple Synthesizer which can generate sound having multiple keys pressed. The main purpose of this ...
Definition: Synthesizer.h:161
int16_t readSample() override
Provides mixed samples of all channels.
Definition: Synthesizer.h:214
void setKeys(AudioActions &actions, SynthesizerKey *p_keys, AudioActions::ActiveLogic activeValue)
Assigns pins to notes - the last SynthesizerKey is marked with an entry containing the note <= 0.
Definition: Synthesizer.h:233
void setMidiName(const char *name)
Defines the midi name.
Definition: Synthesizer.h:241
Vector implementation which provides the most important methods as defined by std::vector....
Definition: Vector.h:21
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:823
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
Definition: Synthesizer.h:251
Arduino GPIO pin to note assossiation.
Definition: Synthesizer.h:149