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