arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
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
14namespace 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){
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
161class 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
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:133
size_t size()
Provides the actual number of defined effects.
Definition AudioEffects.h:45
AudioEffect * findEffect(int id)
Finds an effect by id.
Definition AudioEffects.h:50
void addEffect(AudioEffect &effect)
Adds an effect object (by reference)
Definition AudioEffects.h:27
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.
Sine wave which is based on a fast approximation function.
Definition SoundGenerator.h:235
Base class to define the abstract interface for the sound generating classes.
Definition SoundGenerator.h:28
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:56
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 AudioCodecsBase.h:10
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:53
Definition Synthesizer.h:251
Arduino GPIO pin to note assossiation.
Definition Synthesizer.h:149