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-2022 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
51namespace 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
63static 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
84SineWaveformGenerator::SineWaveformGenerator()
85 : m_phaseInc(0),
86 m_phaseAcc(0),
87 m_frequency(0),
88 m_lastSample(0)
89{
90}
91
92
93void SineWaveformGenerator::setFrequency(int value) {
94 if (m_frequency != value) {
95 m_frequency = value;
96 m_phaseInc = (((uint32_t)m_frequency * 256) << 11) / sampleRate();
97 }
98}
99
100
101int SineWaveformGenerator::getSample() {
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
139SquareWaveformGenerator::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
198TriangleWaveformGenerator::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
250SawtoothWaveformGenerator::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
302NoiseWaveformGenerator::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
343VICNoiseGenerator::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
417SamplesGenerator::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
475SoundGenerator::~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
492void 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)
520bool 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
534SamplesGenerator * 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
548bool 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
573bool 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
608void 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
624void 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
685void 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)
704char 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
743char 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
758void 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:313
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:308
int getSample()
Gets next sample.
Definition: soundgen.cpp:430
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:425
Samples generator.
Definition: soundgen.h:302
int getSample()
Gets next sample.
Definition: soundgen.cpp:267
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:259
void attach(WaveformGenerator *value)
Attaches a waveform generator.
Definition: soundgen.cpp:580
void detach(WaveformGenerator *value)
Detaches a waveform generator.
Definition: soundgen.cpp:595
int volume()
Determines current overall volume.
Definition: soundgen.h:463
SamplesGenerator * playSamples(int8_t const *data, int length, int volume=100, int durationMS=0)
Plays the specified samples.
Definition: soundgen.cpp:534
SoundGenerator(int sampleRate=16000)
Creates an instance of the sound generator. Only one instance is allowed.
Definition: soundgen.cpp:461
bool play(bool value)
Starts or stops playing.
Definition: soundgen.cpp:520
void clear()
Stops playing and removes all attached waveform generators.
Definition: soundgen.cpp:484
int getSample()
Gets next sample.
Definition: soundgen.cpp:164
void setDutyCycle(int dutyCycle)
Sets square wave duty cycle.
Definition: soundgen.cpp:158
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:149
int getSample()
Gets next sample.
Definition: soundgen.cpp:215
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:207
int getSample()
Gets next sample.
Definition: soundgen.cpp:363
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:352
int volume()
Determines current volume.
Definition: soundgen.h:126
uint32_t duration()
Returns number of remaining samples to play.
Definition: soundgen.h:87
uint16_t sampleRate()
Determines the sample rate.
Definition: soundgen.h:158
Base abstract class for waveform generators. A waveform generator can be seen as an audio channel tha...
Definition: soundgen.h:62
uint8_t const * data
This file contains all classes related to FabGL Sound System.