FabGL
ESP32 Display Controller and Graphics Library
soundgen.cpp
1 /*
2  Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3  Copyright (c) 2019-2021 Fabrizio Di Vittorio.
4  All rights reserved.
5 
6 
7 * Please contact fdivitto2013@gmail.com if you need a commercial license.
8 
9 
10 * This library and related software is available under GPL v3.
11 
12  FabGL is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  FabGL is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 
27 
28 
29 #include "freertos/FreeRTOS.h"
30 #include "freertos/timers.h"
31 
32 #include <string.h>
33 #include <ctype.h>
34 #include <math.h>
35 
36 #include "driver/i2s.h"
37 #include "driver/adc.h"
38 #include "esp_adc_cal.h"
39 #if __has_include("hal/i2s_types.h")
40  #include "hal/i2s_types.h"
41 #endif
42 #include "esp_log.h"
43 
44 
45 #include "soundgen.h"
46 
47 
48 #pragma GCC optimize ("O2")
49 
50 
51 namespace fabgl {
52 
53 
54 // maximum value is I2S_SAMPLE_BUFFER_SIZE
55 #define FABGL_SAMPLE_BUFFER_SIZE 32
56 
57 
58 
60 // SineWaveformGenerator
61 
62 
63 static const int8_t sinTable[257] = {
64  0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46,
65  49, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88,
66  90, 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, 115, 116,
67  117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127,
68  127, 127, 127, 127, 126, 126, 126, 125, 125, 124, 123, 122, 122, 121, 120, 118,
69  117, 116, 115, 113, 112, 111, 109, 107, 106, 104, 102, 100, 98, 96, 94, 92,
70  90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51,
71  49, 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 12, 9, 6, 3,
72  0, -3, -6, -9, -12, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46,
73  -49, -51, -54, -57, -60, -63, -65, -68, -71, -73, -76, -78, -81, -83, -85, -88,
74  -90, -92, -94, -96, -98, -100, -102, -104, -106, -107, -109, -111, -112, -113, -115, -116,
75 -117, -118, -120, -121, -122, -122, -123, -124, -125, -125, -126, -126, -126, -127, -127, -127,
76 -127, -127, -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, -121, -120, -118,
77 -117, -116, -115, -113, -112, -111, -109, -107, -106, -104, -102, -100, -98, -96, -94, -92,
78  -90, -88, -85, -83, -81, -78, -76, -73, -71, -68, -65, -63, -60, -57, -54, -51,
79  -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, -9, -6, -3,
80  0,
81 };
82 
83 
84 SineWaveformGenerator::SineWaveformGenerator()
85  : m_phaseInc(0),
86  m_phaseAcc(0),
87  m_frequency(0),
88  m_lastSample(0)
89 {
90 }
91 
92 
94  if (m_frequency != value) {
95  m_frequency = value;
96  m_phaseInc = (((uint32_t)m_frequency * 256) << 11) / sampleRate();
97  }
98 }
99 
100 
102  if (m_frequency == 0 || duration() == 0) {
103  if (m_lastSample > 0)
104  --m_lastSample;
105  else if (m_lastSample < 0)
106  ++m_lastSample;
107  else
108  m_phaseAcc = 0;
109  return m_lastSample;
110  }
111 
112  // get sample (-128...+127)
113  uint32_t index = m_phaseAcc >> 11;
114  double fmul = (double)(m_phaseAcc & 0x7ff) / 2048.0;
115  int sample = sinTable[index] + (sinTable[index + 1] - sinTable[index]) * fmul;
116 
117  // process volume
118  sample = sample * volume() / 127;
119 
120  m_lastSample = sample;
121 
122  m_phaseAcc = (m_phaseAcc + m_phaseInc) & 0x7ffff;
123 
124  decDuration();
125 
126  return sample;
127 }
128 
129 
130 // SineWaveformGenerator
132 
133 
134 
136 // SquareWaveformGenerator
137 
138 
139 SquareWaveformGenerator::SquareWaveformGenerator()
140  : m_phaseInc(0),
141  m_phaseAcc(0),
142  m_frequency(0),
143  m_lastSample(0),
144  m_dutyCycle(127)
145 {
146 }
147 
148 
150  if (m_frequency != value) {
151  m_frequency = value;
152  m_phaseInc = (((uint32_t)m_frequency * 256) << 11) / sampleRate();
153  }
154 }
155 
156 
157 // dutyCycle: 0..255 (255=100%)
159 {
160  m_dutyCycle = dutyCycle;
161 }
162 
163 
165  if (m_frequency == 0 || duration() == 0) {
166  if (m_lastSample > 0)
167  --m_lastSample;
168  else if (m_lastSample < 0)
169  ++m_lastSample;
170  else
171  m_phaseAcc = 0;
172  return m_lastSample;
173  }
174 
175  uint32_t index = m_phaseAcc >> 11;
176  int sample = (index <= m_dutyCycle ? 127 : -127);
177 
178  // process volume
179  sample = sample * volume() / 127;
180 
181  m_lastSample = sample;
182 
183  m_phaseAcc = (m_phaseAcc + m_phaseInc) & 0x7ffff;
184 
185  decDuration();
186 
187  return sample;
188 }
189 
190 // SquareWaveformGenerator
192 
193 
195 // TriangleWaveformGenerator
196 
197 
198 TriangleWaveformGenerator::TriangleWaveformGenerator()
199  : m_phaseInc(0),
200  m_phaseAcc(0),
201  m_frequency(0),
202  m_lastSample(0)
203 {
204 }
205 
206 
208  if (m_frequency != value) {
209  m_frequency = value;
210  m_phaseInc = (((uint32_t)m_frequency * 256) << 11) / sampleRate();
211  }
212 }
213 
214 
216  if (m_frequency == 0 || duration() == 0) {
217  if (m_lastSample > 0)
218  --m_lastSample;
219  else if (m_lastSample < 0)
220  ++m_lastSample;
221  else
222  m_phaseAcc = 0;
223  return m_lastSample;
224  }
225 
226  uint32_t index = m_phaseAcc >> 11;
227  int sample = (index & 0x80 ? -1 : 1) * ((index & 0x3F) * 2 - (index & 0x40 ? 0 : 127));
228 
229  // process volume
230  sample = sample * volume() / 127;
231 
232  m_lastSample = sample;
233 
234  m_phaseAcc = (m_phaseAcc + m_phaseInc) & 0x7ffff;
235 
236  decDuration();
237 
238  return sample;
239 }
240 
241 // TriangleWaveformGenerator
243 
244 
245 
247 // SawtoothWaveformGenerator
248 
249 
250 SawtoothWaveformGenerator::SawtoothWaveformGenerator()
251  : m_phaseInc(0),
252  m_phaseAcc(0),
253  m_frequency(0),
254  m_lastSample(0)
255 {
256 }
257 
258 
260  if (m_frequency != value) {
261  m_frequency = value;
262  m_phaseInc = (((uint32_t)m_frequency * 256) << 11) / sampleRate();
263  }
264 }
265 
266 
268  if (m_frequency == 0 || duration() == 0) {
269  if (m_lastSample > 0)
270  --m_lastSample;
271  else if (m_lastSample < 0)
272  ++m_lastSample;
273  else
274  m_phaseAcc = 0;
275  return m_lastSample;
276  }
277 
278  uint32_t index = m_phaseAcc >> 11;
279  int sample = index - 128;
280 
281  // process volume
282  sample = sample * volume() / 127;
283 
284  m_lastSample = sample;
285 
286  m_phaseAcc = (m_phaseAcc + m_phaseInc) & 0x7ffff;
287 
288  decDuration();
289 
290  return sample;
291 }
292 
293 // TriangleWaveformGenerator
295 
296 
297 
299 // NoiseWaveformGenerator
300 
301 
302 NoiseWaveformGenerator::NoiseWaveformGenerator()
303  : m_noise(0xFAB7)
304 {
305 }
306 
307 
309 {
310 }
311 
312 
314 {
315  if (duration() == 0) {
316  return 0;
317  }
318 
319  // noise generator based on Galois LFSR
320  m_noise = (m_noise >> 1) ^ (-(m_noise & 1) & 0xB400u);
321  int sample = 127 - (m_noise >> 8);
322 
323  // process volume
324  sample = sample * volume() / 127;
325 
326  decDuration();
327 
328  return sample;
329 }
330 
331 
332 // NoiseWaveformGenerator
334 
335 
336 
338 // VICNoiseGenerator
339 // "tries" to emulate VIC6561 noise generator
340 // derived from a reverse engineering VHDL code: http://sleepingelephant.com/ipw-web/bulletin/bb/viewtopic.php?f=11&t=8733&fbclid=IwAR16x6OMMb670bA2ZtYcbB0Zat_X-oHNB0NxxYigPffXa8G_InSIoNAEiPU
341 
342 
343 VICNoiseGenerator::VICNoiseGenerator()
344  : m_frequency(0),
345  m_counter(0),
346  m_LFSR(LFSRINIT),
347  m_outSR(0)
348 {
349 }
350 
351 
353 {
354  if (m_frequency != value) {
355  m_frequency = value >= 127 ? 0 : value;
356  m_LFSR = LFSRINIT;
357  m_counter = 0;
358  m_outSR = 0;
359  }
360 }
361 
362 
364 {
365  if (duration() == 0) {
366  return 0;
367  }
368 
369  const int reduc = CLK / 8 / sampleRate(); // resample to sampleRate() (ie 16000Hz)
370 
371  int sample = 0;
372 
373  for (int i = 0; i < reduc; ++i) {
374 
375  if (m_counter >= 127) {
376 
377  // reset counter
378  m_counter = m_frequency;
379 
380  if (m_LFSR & 1)
381  m_outSR = ((m_outSR << 1) | ~(m_outSR >> 7));
382 
383  m_LFSR <<= 1;
384  int bit3 = (m_LFSR >> 3) & 1;
385  int bit12 = (m_LFSR >> 12) & 1;
386  int bit14 = (m_LFSR >> 14) & 1;
387  int bit15 = (m_LFSR >> 15) & 1;
388  m_LFSR |= (bit3 ^ bit12) ^ (bit14 ^ bit15);
389  } else
390  ++m_counter;
391 
392  sample += m_outSR & 1 ? 127 : -128;
393  }
394 
395  // simple mean of all samples
396 
397  sample = sample / reduc;
398 
399  // process volume
400  sample = sample * volume() / 127;
401 
402  decDuration();
403 
404  return sample;
405 }
406 
407 
408 // VICNoiseGenerator
410 
411 
412 
414 // SamplesGenerator
415 
416 
417 SamplesGenerator::SamplesGenerator(int8_t const * data, int length)
418  : m_data(data),
419  m_length(length),
420  m_index(0)
421 {
422 }
423 
424 
426 {
427 }
428 
429 
431 
432  if (duration() == 0) {
433  return 0;
434  }
435 
436  int sample = m_data[m_index++];
437 
438  if (m_index == m_length)
439  m_index = 0;
440 
441  // process volume
442  sample = sample * volume() / 127;
443 
444  decDuration();
445 
446  return sample;
447 }
448 
449 
450 // NoiseWaveformGenerator
452 
453 
454 
455 
456 
458 // SoundGenerator
459 
460 
462  : m_waveGenTaskHandle(nullptr),
463  m_channels(nullptr),
464  m_sampleBuffer(nullptr),
465  m_volume(100),
466  m_sampleRate(sampleRate),
467  m_play(false),
468  m_state(SoundGeneratorState::Stop)
469 {
470  m_mutex = xSemaphoreCreateMutex();
471  i2s_audio_init();
472 }
473 
474 
475 SoundGenerator::~SoundGenerator()
476 {
477  clear();
478  vTaskDelete(m_waveGenTaskHandle);
479  heap_caps_free(m_sampleBuffer);
480  vSemaphoreDelete(m_mutex);
481 }
482 
483 
485 {
486  AutoSemaphore autoSemaphore(m_mutex);
487  play(false);
488  m_channels = nullptr;
489 }
490 
491 
492 void SoundGenerator::i2s_audio_init()
493 {
494  i2s_config_t i2s_config;
495  i2s_config.mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN);
496  i2s_config.sample_rate = m_sampleRate;
497  i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
498  #if FABGL_ESP_IDF_VERSION <= FABGL_ESP_IDF_VERSION_VAL(4, 1, 1)
499  i2s_config.communication_format = (i2s_comm_format_t) I2S_COMM_FORMAT_I2S_MSB;
500  #else
501  i2s_config.communication_format = I2S_COMM_FORMAT_STAND_I2S;
502  #endif
503  i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
504  i2s_config.intr_alloc_flags = 0;
505  i2s_config.dma_buf_count = 2;
506  i2s_config.dma_buf_len = FABGL_SAMPLE_BUFFER_SIZE * sizeof(uint16_t);
507  i2s_config.use_apll = 0;
508  i2s_config.tx_desc_auto_clear = 0;
509  i2s_config.fixed_mclk = 0;
510  // install and start i2s driver
511  i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
512  // init DAC pad
513  i2s_set_dac_mode(I2S_DAC_CHANNEL_RIGHT_EN); // GPIO25
514 
515  m_sampleBuffer = (uint16_t*) heap_caps_malloc(FABGL_SAMPLE_BUFFER_SIZE * sizeof(uint16_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
516 }
517 
518 
519 // the same of forcePlay(), but fill also output DMA with 127s, making output mute (and making "bumping" effect)
520 bool SoundGenerator::play(bool value)
521 {
522  AutoSemaphore autoSemaphore(m_mutex);
523  m_play = value;
524  if (actualPlaying() != value) {
525  bool r = forcePlay(value);
526  if (!value)
527  mutizeOutput();
528  return r;
529  } else
530  return value;
531 }
532 
533 
534 SamplesGenerator * SoundGenerator::playSamples(int8_t const * data, int length, int volume, int durationMS)
535 {
536  auto sgen = new SamplesGenerator(data, length);
537  attach(sgen);
538  sgen->setAutoDestroy(true);
539  if (durationMS > -1)
540  sgen->setDuration(durationMS > 0 ? (m_sampleRate / 1000 * durationMS) : length );
541  sgen->setVolume(volume);
542  sgen->enable(true);
543  play(true);
544  return sgen;
545 }
546 
547 
548 bool SoundGenerator::forcePlay(bool value)
549 {
550  bool isPlaying = actualPlaying();
551  if (value) {
552  // play
553  if (!isPlaying) {
554  if (!m_waveGenTaskHandle)
555  xTaskCreate(waveGenTask, "", WAVEGENTASK_STACK_SIZE, this, 5, &m_waveGenTaskHandle);
556  m_state = SoundGeneratorState::RequestToPlay;
557  xTaskNotifyGive(m_waveGenTaskHandle);
558  }
559  } else {
560  // stop
561  if (isPlaying) {
562  // request task to suspend itself when possible
563  m_state = SoundGeneratorState::RequestToStop;
564  // wait for task switch to suspend state (TODO: is there a better way?)
565  while (m_state != SoundGeneratorState::Stop)
566  vTaskDelay(1);
567  }
568  }
569  return isPlaying;
570 }
571 
572 
573 bool SoundGenerator::actualPlaying()
574 {
575  return m_waveGenTaskHandle && m_state == SoundGeneratorState::Playing;
576 }
577 
578 
579 // does NOT take ownership of the waveform generator
581 {
582  AutoSemaphore autoSemaphore(m_mutex);
583 
584  bool isPlaying = forcePlay(false);
585 
586  value->setSampleRate(m_sampleRate);
587 
588  value->next = m_channels;
589  m_channels = value;
590 
591  forcePlay(isPlaying || m_play);
592 }
593 
594 
596 {
597  if (!value)
598  return;
599 
600  AutoSemaphore autoSemaphore(m_mutex);
601 
602  bool isPlaying = forcePlay(false);
603  detachNoSuspend(value);
604  forcePlay(isPlaying);
605 }
606 
607 
608 void SoundGenerator::detachNoSuspend(WaveformGenerator * value)
609 {
610  for (WaveformGenerator * c = m_channels, * prev = nullptr; c; prev = c, c = c->next) {
611  if (c == value) {
612  if (prev)
613  prev->next = c->next;
614  else
615  m_channels = c->next;
616  if (value->autoDestroy())
617  delete value;
618  break;
619  }
620  }
621 }
622 
623 
624 void IRAM_ATTR SoundGenerator::waveGenTask(void * arg)
625 {
626  SoundGenerator * soundGenerator = (SoundGenerator*) arg;
627 
628  i2s_set_clk(I2S_NUM_0, soundGenerator->m_sampleRate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO);
629 
630  uint16_t * buf = soundGenerator->m_sampleBuffer;
631 
632  // number of mute (without channels to play) cycles
633  int muteCyclesCount = 0;
634 
635  while (true) {
636 
637  // suspend?
638  if (soundGenerator->m_state == SoundGeneratorState::RequestToStop || soundGenerator->m_state == SoundGeneratorState::Stop) {
639  soundGenerator->m_state = SoundGeneratorState::Stop;
640  while (soundGenerator->m_state == SoundGeneratorState::Stop)
641  ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // wait for "give"
642  }
643 
644  // mutize output?
645  if (soundGenerator->m_channels == nullptr && muteCyclesCount >= 8) {
646  soundGenerator->m_state = SoundGeneratorState::Stop;
647  while (soundGenerator->m_state == SoundGeneratorState::Stop)
648  ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // wait for "give"
649  }
650 
651  soundGenerator->m_state = SoundGeneratorState::Playing;
652 
653  int mainVolume = soundGenerator->volume();
654 
655  for (int i = 0; i < FABGL_SAMPLE_BUFFER_SIZE; ++i) {
656  int sample = 0, tvol = 0;
657  for (auto g = soundGenerator->m_channels; g; ) {
658  if (g->enabled()) {
659  sample += g->getSample();
660  tvol += g->volume();
661  } else if (g->duration() == 0 && g->autoDetach()) {
662  auto curr = g;
663  g = g->next; // setup next item before detaching this one
664  soundGenerator->detachNoSuspend(curr);
665  continue; // bypass "g = g->next;"
666  }
667  g = g->next;
668  }
669 
670  int avol = tvol ? imin(127, 127 * 127 / tvol) : 127;
671  sample = sample * avol / 127;
672  sample = sample * mainVolume / 127;
673 
674  buf[i + (i & 1 ? -1 : 1)] = (127 + sample) << 8;
675  }
676 
677  size_t bytes_written;
678  i2s_write(I2S_NUM_0, buf, FABGL_SAMPLE_BUFFER_SIZE * sizeof(uint16_t), &bytes_written, portMAX_DELAY);
679 
680  muteCyclesCount = soundGenerator->m_channels == nullptr ? muteCyclesCount + 1 : 0;
681  }
682 }
683 
684 
685 void SoundGenerator::mutizeOutput()
686 {
687  for (int i = 0; i < FABGL_SAMPLE_BUFFER_SIZE; ++i)
688  m_sampleBuffer[i] = 127 << 8;
689  size_t bytes_written;
690  for (int i = 0; i < 4; ++i)
691  i2s_write(I2S_NUM_0, m_sampleBuffer, FABGL_SAMPLE_BUFFER_SIZE * sizeof(uint16_t), &bytes_written, portMAX_DELAY);
692 }
693 
694 
695 // SoundGenerator
697 
698 
699 
700 /*
701 
702 // note (C,D,E,F,G,A,B) + [#,b] + octave (2..7) + space + tempo (99..1)
703 // pause (P) + space + tempo (99.1)
704 char const * noteToFreq(char const * note, int * freq)
705 {
706  uint16_t NIDX2FREQ[][12] = { { 66, 70, 74, 78, 83, 88, 93, 98, 104, 110, 117, 124 }, // 2
707  { 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247 }, // 3
708  { 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494 }, // 4
709  { 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988 }, // 5
710  { 1046, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976 }, // 6
711  { 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951 }, // 7
712  };
713  uint8_t NNAME2NIDX[] = {9, 11, 0, 2, 4, 5, 7}; // A, B, C, D, E, F, G
714  *freq = 0;
715  while (*note && *note == ' ')
716  ++note;
717  if (*note == 0)
718  return note;
719  int noteIndex = (*note >= 'A' && *note <= 'G' ? NNAME2NIDX[*note - 'A'] : -2); // -2 = pause
720  ++note;
721  if (*note == '#') {
722  ++noteIndex;
723  ++note;
724  } else if (*note == 'b') {
725  --noteIndex;
726  ++note;
727  }
728  int octave = *note - '0';
729  ++note;
730  if (noteIndex == -1) {
731  noteIndex = 11;
732  --octave;
733  } else if (noteIndex == 12) {
734  noteIndex = 0;
735  ++octave;
736  }
737  if (noteIndex >= 0 && noteIndex <= 11 && octave >= 2 && octave <= 7)
738  *freq = NIDX2FREQ[octave - 2][noteIndex];
739  return note;
740 }
741 
742 
743 char const * noteToDelay(char const * note, int * delayMS)
744 {
745  *delayMS = 0;
746  while (*note && *note == ' ')
747  ++note;
748  if (*note == 0)
749  return note;
750  int val = atoi(note);
751  if (val > 0)
752  *delayMS = 1000 / val;
753  return note + (val > 9 ? 2 : 1);
754 }
755 
756 
757 
758 void play_task(void*arg)
759 {
760  const char * music = "A4 4 A4 4 A#4 4 C5 4 C5 4 A#4 4 A4 4 G4 4 F4 4 F4 4 G4 4 A4 4 A4 2 G4 16 G4 2 P 8 "
761  "A4 4 A4 4 A#4 4 C5 4 C5 4 A#4 4 A4 4 G4 4 F4 4 F4 4 G4 4 A4 4 G4 2 F4 16 F4 2 P 8";
762  while (true) {
763  if (Play) {
764  const char * m = music;
765  while (*m && Play) {
766  int freq, delms;
767  m = noteToFreq(m, &freq);
768  m = noteToDelay(m, &delms);
769  frequency = freq;
770  delay(delms);
771  frequency = 0;
772  delay(25);
773  }
774  Play = false;
775  } else
776  delay(100);
777  }
778 }
779 
780 
781 */
782 
783 
784 
785 
786 
787 
788 
789 
790 } // end of namespace
791 
int getSample()
Gets next sample.
Definition: soundgen.cpp:164
SamplesGenerator * playSamples(int8_t const *data, int length, int volume=100, int durationMS=0)
Plays the specified samples.
Definition: soundgen.cpp:534
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:308
Samples generator.
Definition: soundgen.h:302
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:259
int getSample()
Gets next sample.
Definition: soundgen.cpp:215
Base abstract class for waveform generators. A waveform generator can be seen as an audio channel tha...
Definition: soundgen.h:62
int getSample()
Gets next sample.
Definition: soundgen.cpp:430
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:425
uint8_t const * data
uint32_t duration()
Returns number of remaining samples to play.
Definition: soundgen.h:87
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:207
void attach(WaveformGenerator *value)
Attaches a waveform generator.
Definition: soundgen.cpp:580
int getSample()
Gets next sample.
Definition: soundgen.cpp:363
int volume()
Determines current overall volume.
Definition: soundgen.h:463
Definition: canvas.cpp:36
void clear()
Stops playing and removes all attached waveform generators.
Definition: soundgen.cpp:484
void detach(WaveformGenerator *value)
Detaches a waveform generator.
Definition: soundgen.cpp:595
bool play(bool value)
Starts or stops playing.
Definition: soundgen.cpp:520
int getSample()
Gets next sample.
Definition: soundgen.cpp:267
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:352
int getSample()
Gets next sample.
Definition: soundgen.cpp:101
This file contains all classes related to FabGL Sound System.
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:93
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:149
void setDutyCycle(int dutyCycle)
Sets square wave duty cycle.
Definition: soundgen.cpp:158
uint16_t sampleRate()
Determines the sample rate.
Definition: soundgen.h:158
int getSample()
Gets next sample.
Definition: soundgen.cpp:313
int volume()
Determines current volume.
Definition: soundgen.h:126
SoundGenerator(int sampleRate=16000)
Creates an instance of the sound generator. Only one instance is allowed.
Definition: soundgen.cpp:461