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/dac.h"
37#include "soc/i2s_reg.h"
38#include "driver/periph_ctrl.h"
39#include "soc/rtc.h"
40#include <soc/sens_reg.h>
41#include "esp_log.h"
42#include "driver/sigmadelta.h"
43
44
45#include "soundgen.h"
46
47
48#pragma GCC optimize ("O2")
49
50
51namespace fabgl {
52
53
54
56// SineWaveformGenerator
57
58
59static const int8_t sinTable[257] = {
60 0, 3, 6, 9, 12, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 46,
61 49, 51, 54, 57, 60, 63, 65, 68, 71, 73, 76, 78, 81, 83, 85, 88,
62 90, 92, 94, 96, 98, 100, 102, 104, 106, 107, 109, 111, 112, 113, 115, 116,
63 117, 118, 120, 121, 122, 122, 123, 124, 125, 125, 126, 126, 126, 127, 127, 127,
64 127, 127, 127, 127, 126, 126, 126, 125, 125, 124, 123, 122, 122, 121, 120, 118,
65 117, 116, 115, 113, 112, 111, 109, 107, 106, 104, 102, 100, 98, 96, 94, 92,
66 90, 88, 85, 83, 81, 78, 76, 73, 71, 68, 65, 63, 60, 57, 54, 51,
67 49, 46, 43, 40, 37, 34, 31, 28, 25, 22, 19, 16, 12, 9, 6, 3,
68 0, -3, -6, -9, -12, -16, -19, -22, -25, -28, -31, -34, -37, -40, -43, -46,
69 -49, -51, -54, -57, -60, -63, -65, -68, -71, -73, -76, -78, -81, -83, -85, -88,
70 -90, -92, -94, -96, -98, -100, -102, -104, -106, -107, -109, -111, -112, -113, -115, -116,
71-117, -118, -120, -121, -122, -122, -123, -124, -125, -125, -126, -126, -126, -127, -127, -127,
72-127, -127, -127, -127, -126, -126, -126, -125, -125, -124, -123, -122, -122, -121, -120, -118,
73-117, -116, -115, -113, -112, -111, -109, -107, -106, -104, -102, -100, -98, -96, -94, -92,
74 -90, -88, -85, -83, -81, -78, -76, -73, -71, -68, -65, -63, -60, -57, -54, -51,
75 -49, -46, -43, -40, -37, -34, -31, -28, -25, -22, -19, -16, -12, -9, -6, -3,
76 0,
77};
78
79
80SineWaveformGenerator::SineWaveformGenerator()
81 : m_phaseInc(0),
82 m_phaseAcc(0),
83 m_frequency(0),
84 m_lastSample(0)
85{
86}
87
88
89void SineWaveformGenerator::setFrequency(int value) {
90 if (m_frequency != value) {
91 m_frequency = value;
92 m_phaseInc = (((uint32_t)m_frequency * 256) << 11) / sampleRate();
93 }
94}
95
96
97int SineWaveformGenerator::getSample() {
98 if (m_frequency == 0 || duration() == 0) {
99 if (m_lastSample > 0)
100 --m_lastSample;
101 else if (m_lastSample < 0)
102 ++m_lastSample;
103 else
104 m_phaseAcc = 0;
105 return m_lastSample;
106 }
107
108 // get sample (-128...+127)
109 uint32_t index = m_phaseAcc >> 11;
110 int sample = sinTable[index];
111
112 // process volume
113 sample = sample * volume() / 127;
114
115 m_lastSample = sample;
116
117 m_phaseAcc = (m_phaseAcc + m_phaseInc) & 0x7ffff;
118
119 decDuration();
120
121 return sample;
122}
123
124
125// SineWaveformGenerator
127
128
129
131// SquareWaveformGenerator
132
133
134SquareWaveformGenerator::SquareWaveformGenerator()
135 : m_phaseInc(0),
136 m_phaseAcc(0),
137 m_frequency(0),
138 m_lastSample(0),
139 m_dutyCycle(127)
140{
141}
142
143
145 if (m_frequency != value) {
146 m_frequency = value;
147 m_phaseInc = (((uint32_t)m_frequency * 256) << 11) / sampleRate();
148 }
149}
150
151
152// dutyCycle: 0..255 (255=100%)
154{
155 m_dutyCycle = dutyCycle;
156}
157
158
160 if (m_frequency == 0 || duration() == 0) {
161 if (m_lastSample > 0)
162 --m_lastSample;
163 else if (m_lastSample < 0)
164 ++m_lastSample;
165 else
166 m_phaseAcc = 0;
167 return m_lastSample;
168 }
169
170 uint32_t index = m_phaseAcc >> 11;
171 int sample = (index <= m_dutyCycle ? 127 : -127);
172
173 // process volume
174 sample = sample * volume() / 127;
175
176 m_lastSample = sample;
177
178 m_phaseAcc = (m_phaseAcc + m_phaseInc) & 0x7ffff;
179
180 decDuration();
181
182 return sample;
183}
184
185// SquareWaveformGenerator
187
188
190// TriangleWaveformGenerator
191
192
193TriangleWaveformGenerator::TriangleWaveformGenerator()
194 : m_phaseInc(0),
195 m_phaseAcc(0),
196 m_frequency(0),
197 m_lastSample(0)
198{
199}
200
201
203 if (m_frequency != value) {
204 m_frequency = value;
205 m_phaseInc = (((uint32_t)m_frequency * 256) << 11) / sampleRate();
206 }
207}
208
209
211 if (m_frequency == 0 || duration() == 0) {
212 if (m_lastSample > 0)
213 --m_lastSample;
214 else if (m_lastSample < 0)
215 ++m_lastSample;
216 else
217 m_phaseAcc = 0;
218 return m_lastSample;
219 }
220
221 uint32_t index = m_phaseAcc >> 11;
222 int sample = (index & 0x80 ? -1 : 1) * ((index & 0x3F) * 2 - (index & 0x40 ? 0 : 127));
223
224 // process volume
225 sample = sample * volume() / 127;
226
227 m_lastSample = sample;
228
229 m_phaseAcc = (m_phaseAcc + m_phaseInc) & 0x7ffff;
230
231 decDuration();
232
233 return sample;
234}
235
236// TriangleWaveformGenerator
238
239
240
242// SawtoothWaveformGenerator
243
244
245SawtoothWaveformGenerator::SawtoothWaveformGenerator()
246 : m_phaseInc(0),
247 m_phaseAcc(0),
248 m_frequency(0),
249 m_lastSample(0)
250{
251}
252
253
255 if (m_frequency != value) {
256 m_frequency = value;
257 m_phaseInc = (((uint32_t)m_frequency * 256) << 11) / sampleRate();
258 }
259}
260
261
263 if (m_frequency == 0 || duration() == 0) {
264 if (m_lastSample > 0)
265 --m_lastSample;
266 else if (m_lastSample < 0)
267 ++m_lastSample;
268 else
269 m_phaseAcc = 0;
270 return m_lastSample;
271 }
272
273 uint32_t index = m_phaseAcc >> 11;
274 int sample = index - 128;
275
276 // process volume
277 sample = sample * volume() / 127;
278
279 m_lastSample = sample;
280
281 m_phaseAcc = (m_phaseAcc + m_phaseInc) & 0x7ffff;
282
283 decDuration();
284
285 return sample;
286}
287
288// TriangleWaveformGenerator
290
291
292
294// NoiseWaveformGenerator
295
296
297NoiseWaveformGenerator::NoiseWaveformGenerator()
298 : m_noise(0xFAB7)
299{
300}
301
302
304{
305}
306
307
309{
310 if (duration() == 0) {
311 return 0;
312 }
313
314 // noise generator based on Galois LFSR
315 m_noise = (m_noise >> 1) ^ (-(m_noise & 1) & 0xB400u);
316 int sample = 127 - (m_noise >> 8);
317
318 // process volume
319 sample = sample * volume() / 127;
320
321 decDuration();
322
323 return sample;
324}
325
326
327// NoiseWaveformGenerator
329
330
331
333// VICNoiseGenerator
334// "tries" to emulate VIC6561 noise generator
335// 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
336
337
338VICNoiseGenerator::VICNoiseGenerator()
339 : m_frequency(0),
340 m_counter(0),
341 m_LFSR(LFSRINIT),
342 m_outSR(0)
343{
344}
345
346
348{
349 if (m_frequency != value) {
350 m_frequency = value >= 127 ? 0 : value;
351 m_LFSR = LFSRINIT;
352 m_counter = 0;
353 m_outSR = 0;
354 }
355}
356
357
359{
360 if (duration() == 0) {
361 return 0;
362 }
363
364 const int reduc = CLK / 8 / sampleRate(); // resample to sampleRate() (ie 16000Hz)
365
366 int sample = 0;
367
368 for (int i = 0; i < reduc; ++i) {
369
370 if (m_counter >= 127) {
371
372 // reset counter
373 m_counter = m_frequency;
374
375 if (m_LFSR & 1)
376 m_outSR = ((m_outSR << 1) | ~(m_outSR >> 7));
377
378 m_LFSR <<= 1;
379 int bit3 = (m_LFSR >> 3) & 1;
380 int bit12 = (m_LFSR >> 12) & 1;
381 int bit14 = (m_LFSR >> 14) & 1;
382 int bit15 = (m_LFSR >> 15) & 1;
383 m_LFSR |= (bit3 ^ bit12) ^ (bit14 ^ bit15);
384 } else
385 ++m_counter;
386
387 sample += m_outSR & 1 ? 127 : -128;
388 }
389
390 // simple mean of all samples
391
392 sample = sample / reduc;
393
394 // process volume
395 sample = sample * volume() / 127;
396
397 decDuration();
398
399 return sample;
400}
401
402
403// VICNoiseGenerator
405
406
407
409// SamplesGenerator
410
411
412SamplesGenerator::SamplesGenerator(int8_t const * data, int length)
413 : m_data(data),
414 m_length(length),
415 m_index(0)
416{
417}
418
419
421{
422}
423
424
426
427 if (duration() == 0) {
428 return 0;
429 }
430
431 int sample = m_data[m_index++];
432
433 if (m_index == m_length)
434 m_index = 0;
435
436 // process volume
437 sample = sample * volume() / 127;
438
439 decDuration();
440
441 return sample;
442}
443
444
445// SamplesGenerator
447
448
449
451// SoundGenerator
452
453
454SoundGenerator::SoundGenerator(int sampleRate, gpio_num_t gpio, SoundGenMethod genMethod)
455 : m_channels(nullptr),
456 m_sampleBuffer{0},
457 m_volume(100),
458 m_sampleRate(sampleRate),
459 m_play(false),
460 m_gpio(gpio),
461 m_isr_handle(nullptr),
462 m_DMAChain(nullptr),
463 m_genMethod(genMethod),
464 m_initDone(false),
465 m_timerHandle(nullptr)
466{
467}
468
469
470SoundGenerator::~SoundGenerator()
471{
472 clear();
473
474 if (m_isr_handle) {
475 // cleanup DAC mode
476 periph_module_disable(PERIPH_I2S0_MODULE);
477 esp_intr_free(m_isr_handle);
478 for (int i = 0; i < 2; ++i)
479 heap_caps_free(m_sampleBuffer[i]);
480 heap_caps_free((void*)m_DMAChain);
481 }
482
483 if (m_timerHandle) {
484 // cleanup sigmadelta mode
485 esp_timer_stop(m_timerHandle);
486 esp_timer_delete(m_timerHandle);
487 m_timerHandle = nullptr;
488 }
489
490 #ifdef FABGL_EMULATED
491 SDL_CloseAudioDevice(m_device);
492 #endif
493
494}
495
496
498{
499 play(false);
500 m_channels = nullptr;
501}
502
503
504void SoundGenerator::setDMANode(int index, volatile uint16_t * buf, int len)
505{
506 m_DMAChain[index].eof = 1; // always generate interrupt
507 m_DMAChain[index].sosf = 0;
508 m_DMAChain[index].owner = 1;
509 m_DMAChain[index].qe.stqe_next = (lldesc_t *) (m_DMAChain + index + 1);
510 m_DMAChain[index].offset = 0;
511 m_DMAChain[index].size = len * sizeof(uint16_t);
512 m_DMAChain[index].length = len * sizeof(uint16_t);
513 m_DMAChain[index].buf = (uint8_t*) buf;
514}
515
516
517void SoundGenerator::dac_init()
518{
519 m_DMAChain = (volatile lldesc_t *) heap_caps_malloc(2 * sizeof(lldesc_t), MALLOC_CAP_DMA);
520
521 for (int i = 0; i < 2; ++i) {
522 m_sampleBuffer[i] = (uint16_t *) heap_caps_malloc(FABGL_SOUNDGEN_SAMPLE_BUFFER_SIZE * sizeof(uint16_t), MALLOC_CAP_DMA);
523 for (int j = 0; j < FABGL_SOUNDGEN_SAMPLE_BUFFER_SIZE; ++j)
524 m_sampleBuffer[i][j] = 0x7f00;
525 setDMANode(i, m_sampleBuffer[i], FABGL_SOUNDGEN_SAMPLE_BUFFER_SIZE);
526 }
527 m_DMAChain[1].sosf = 1;
528 m_DMAChain[1].qe.stqe_next = (lldesc_t *) m_DMAChain; // closes DMA chain
529
530 periph_module_enable(PERIPH_I2S0_MODULE);
531
532 // Initialize I2S device
533 I2S0.conf.tx_reset = 1;
534 I2S0.conf.tx_reset = 0;
535
536 // Reset DMA
537 I2S0.lc_conf.in_rst = 1;
538 I2S0.lc_conf.in_rst = 0;
539
540 // Reset FIFO
541 I2S0.conf.rx_fifo_reset = 1;
542 I2S0.conf.rx_fifo_reset = 0;
543
544 I2S0.conf_chan.tx_chan_mod = (m_gpio == GPIO_NUM_25 ? 3 : 4);
545
546 I2S0.fifo_conf.tx_fifo_mod_force_en = 1;
547 I2S0.fifo_conf.tx_fifo_mod = 1;
548 I2S0.fifo_conf.dscr_en = 1;
549
550 I2S0.conf.tx_mono = 1;
551 I2S0.conf.tx_start = 0;
552 I2S0.conf.tx_msb_right = 1;
553 I2S0.conf.tx_right_first = 1;
554 I2S0.conf.tx_slave_mod = 0;
555 I2S0.conf.tx_short_sync = 0;
556 I2S0.conf.tx_msb_shift = 0;
557
558 I2S0.conf2.lcd_en = 1;
559 I2S0.conf2.camera_en = 0;
560
561 int a, b, num, m;
562 m_sampleRate = calcI2STimingParams(m_sampleRate, &a, &b, &num, &m);
563 I2S0.clkm_conf.clka_en = 0;
564 I2S0.clkm_conf.clkm_div_a = a;
565 I2S0.clkm_conf.clkm_div_b = b;
566 I2S0.clkm_conf.clkm_div_num = num;
567 I2S0.sample_rate_conf.tx_bck_div_num = m;
568
569 I2S0.sample_rate_conf.tx_bits_mod = 16;
570
571 if (m_isr_handle == nullptr) {
572 esp_intr_alloc_pinnedToCore(ETS_I2S0_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, ISRHandler, this, &m_isr_handle, CoreUsage::quietCore());
573 I2S0.int_clr.val = 0xFFFFFFFF;
574 I2S0.int_ena.out_eof = 1;
575 }
576
577 I2S0.out_link.addr = (uintptr_t) m_DMAChain;
578 I2S0.out_link.start = 1;
579 I2S0.conf.tx_start = 1;
580
581 dac_i2s_enable();
582 dac_output_enable(m_gpio == GPIO_NUM_25 ? DAC_CHANNEL_1 : DAC_CHANNEL_2);
583}
584
585
586void SoundGenerator::sigmadelta_init()
587{
588 sigmadelta_config_t sigmadelta_cfg;
589 sigmadelta_cfg.channel = SIGMADELTA_CHANNEL_0;
590 sigmadelta_cfg.sigmadelta_prescale = 10;
591 sigmadelta_cfg.sigmadelta_duty = 0;
592 sigmadelta_cfg.sigmadelta_gpio = m_gpio;
593 sigmadelta_config(&sigmadelta_cfg);
594
595 esp_timer_create_args_t args = { };
596 args.callback = timerHandler;
597 args.arg = this;
598 args.dispatch_method = ESP_TIMER_TASK;
599 esp_timer_create(&args, &m_timerHandle);
600}
601
602
603#ifdef FABGL_EMULATED
604void SoundGenerator::sdl_init()
605{
606 SDL_AudioSpec wantSpec, haveSpec;
607 SDL_zero(wantSpec);
608 wantSpec.freq = m_sampleRate;
609 wantSpec.format = AUDIO_U8;
610 wantSpec.channels = 1;
611 wantSpec.samples = 2048;
612 wantSpec.callback = SDLAudioCallback;
613 wantSpec.userdata = this;
614 m_device = SDL_OpenAudioDevice(NULL, 0, &wantSpec, &haveSpec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
615 m_sampleRate = haveSpec.freq;
616}
617#endif
618
619
620void SoundGenerator::init()
621{
622 if (!m_initDone) {
623 // handle automatic paramters
624 if (m_genMethod == SoundGenMethod::Auto)
625 m_genMethod = CurrentVideoMode::get() == VideoMode::CVBS ? SoundGenMethod::SigmaDelta : SoundGenMethod::DAC;
626 if (m_gpio == GPIO_AUTO)
627 m_gpio = m_genMethod == SoundGenMethod::DAC ? GPIO_NUM_25 : GPIO_NUM_23;
628
629 // actual init
630 if (m_genMethod == SoundGenMethod::DAC)
631 dac_init();
632 else
633 sigmadelta_init();
634
635 #ifdef FABGL_EMULATED
636 sdl_init();
637 #endif
638
639 m_initDone = true;
640 }
641}
642
643
644bool SoundGenerator::play(bool value)
645{
646 if (value != m_play) {
647 init();
648
649 if (m_genMethod == SoundGenMethod::DAC) {
650 I2S0.conf.tx_start = value;
651 } else {
652 if (value)
653 esp_timer_start_periodic(m_timerHandle, 1000000 / m_sampleRate);
654 else
655 esp_timer_stop(m_timerHandle);
656 }
657
658 #ifdef FABGL_EMULATED
659 SDL_PauseAudioDevice(m_device, !value);
660 #endif
661
662 m_play = value;
663 return !value;
664 } else
665 return value;
666}
667
668
669SamplesGenerator * SoundGenerator::playSamples(int8_t const * data, int length, int volume, int durationMS)
670{
671 auto sgen = new SamplesGenerator(data, length);
672 attach(sgen);
673 sgen->setAutoDestroy(true);
674 if (durationMS > -1)
675 sgen->setDuration(durationMS > 0 ? (m_sampleRate / 1000 * durationMS) : length );
676 sgen->setVolume(volume);
677 sgen->enable(true);
678 play(true);
679 return sgen;
680}
681
682
683// does NOT take ownership of the waveform generator
685{
686 bool isPlaying = play(false);
687
688 value->setSampleRate(m_sampleRate);
689
690 value->next = m_channels;
691 m_channels = value;
692
693 play(isPlaying);
694}
695
696
698{
699 if (!value)
700 return;
701
702 bool isPlaying = play(false);
703 detachNoSuspend(value);
704 play(isPlaying);
705}
706
707
708void SoundGenerator::detachNoSuspend(WaveformGenerator * value)
709{
710 for (WaveformGenerator * c = m_channels, * prev = nullptr; c; prev = c, c = c->next) {
711 if (c == value) {
712 if (prev)
713 prev->next = c->next;
714 else
715 m_channels = c->next;
716 if (value->autoDestroy())
717 delete value;
718 break;
719 }
720 }
721}
722
723
724int IRAM_ATTR SoundGenerator::getSample()
725{
726 int sample = 0, tvol = 0;
727 for (auto g = m_channels; g; ) {
728 if (g->enabled()) {
729 sample += g->getSample();
730 tvol += g->volume();
731 } else if (g->duration() == 0 && g->autoDetach()) {
732 auto curr = g;
733 g = g->next; // setup next item before detaching this one
734 detachNoSuspend(curr);
735 continue; // bypass "g = g->next;"
736 }
737 g = g->next;
738 }
739
740 int avol = tvol ? imin(127, 127 * 127 / tvol) : 127;
741 sample = sample * avol / 127;
742 sample = sample * volume() / 127;
743
744 return sample;
745}
746
747
748// used by DAC generator
749void IRAM_ATTR SoundGenerator::ISRHandler(void * arg)
750{
751 if (I2S0.int_st.out_eof) {
752
753 auto soundGenerator = (SoundGenerator *) arg;
754 auto desc = (volatile lldesc_t*) I2S0.out_eof_des_addr;
755
756 auto buf = (uint16_t *) soundGenerator->m_sampleBuffer[desc->sosf];
757
758 for (int i = 0; i < FABGL_SOUNDGEN_SAMPLE_BUFFER_SIZE; ++i)
759 buf[i ^ 1] = (soundGenerator->getSample() + 127) << 8;
760
761 }
762 I2S0.int_clr.val = I2S0.int_st.val;
763}
764
765
766// used by sigma-delta generator
767void SoundGenerator::timerHandler(void * args)
768{
769 auto soundGenerator = (SoundGenerator *) args;
770
771 sigmadelta_set_duty(SIGMADELTA_CHANNEL_0, soundGenerator->getSample());
772}
773
774
775#ifdef FABGL_EMULATED
776void SoundGenerator::SDLAudioCallback(void * data, Uint8 * buffer, int length)
777{
778 auto soundGenerator = (SoundGenerator *) data;
779
780 for (int i = 0; i < length; ++i)
781 buffer[i] = soundGenerator->getSample() + 127;
782}
783#endif
784
785
786// SoundGenerator
788
789
790
791/*
792
793// note (C,D,E,F,G,A,B) + [#,b] + octave (2..7) + space + tempo (99..1)
794// pause (P) + space + tempo (99.1)
795char const * noteToFreq(char const * note, int * freq)
796{
797 uint16_t NIDX2FREQ[][12] = { { 66, 70, 74, 78, 83, 88, 93, 98, 104, 110, 117, 124 }, // 2
798 { 131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247 }, // 3
799 { 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494 }, // 4
800 { 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988 }, // 5
801 { 1046, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976 }, // 6
802 { 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951 }, // 7
803 };
804 uint8_t NNAME2NIDX[] = {9, 11, 0, 2, 4, 5, 7}; // A, B, C, D, E, F, G
805 *freq = 0;
806 while (*note && *note == ' ')
807 ++note;
808 if (*note == 0)
809 return note;
810 int noteIndex = (*note >= 'A' && *note <= 'G' ? NNAME2NIDX[*note - 'A'] : -2); // -2 = pause
811 ++note;
812 if (*note == '#') {
813 ++noteIndex;
814 ++note;
815 } else if (*note == 'b') {
816 --noteIndex;
817 ++note;
818 }
819 int octave = *note - '0';
820 ++note;
821 if (noteIndex == -1) {
822 noteIndex = 11;
823 --octave;
824 } else if (noteIndex == 12) {
825 noteIndex = 0;
826 ++octave;
827 }
828 if (noteIndex >= 0 && noteIndex <= 11 && octave >= 2 && octave <= 7)
829 *freq = NIDX2FREQ[octave - 2][noteIndex];
830 return note;
831}
832
833
834char const * noteToDelay(char const * note, int * delayMS)
835{
836 *delayMS = 0;
837 while (*note && *note == ' ')
838 ++note;
839 if (*note == 0)
840 return note;
841 int val = atoi(note);
842 if (val > 0)
843 *delayMS = 1000 / val;
844 return note + (val > 9 ? 2 : 1);
845}
846
847
848
849void play_task(void*arg)
850{
851 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 "
852 "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";
853 while (true) {
854 if (Play) {
855 const char * m = music;
856 while (*m && Play) {
857 int freq, delms;
858 m = noteToFreq(m, &freq);
859 m = noteToDelay(m, &delms);
860 frequency = freq;
861 delay(delms);
862 frequency = 0;
863 delay(25);
864 }
865 Play = false;
866 } else
867 delay(100);
868 }
869}
870
871
872*/
873
874
875
876
877
878
879
880
881} // end of namespace
882
int getSample()
Gets next sample.
Definition: soundgen.cpp:308
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:303
int getSample()
Gets next sample.
Definition: soundgen.cpp:425
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:420
Samples generator.
Definition: soundgen.h:310
int getSample()
Gets next sample.
Definition: soundgen.cpp:262
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:254
void attach(WaveformGenerator *value)
Attaches a waveform generator.
Definition: soundgen.cpp:684
void detach(WaveformGenerator *value)
Detaches a waveform generator.
Definition: soundgen.cpp:697
SoundGenerator(int sampleRate=16384, gpio_num_t gpio=GPIO_AUTO, SoundGenMethod genMethod=SoundGenMethod::Auto)
Creates an instance of the sound generator. Only one instance is allowed.
Definition: soundgen.cpp:454
int volume()
Determines current overall volume.
Definition: soundgen.h:484
SamplesGenerator * playSamples(int8_t const *data, int length, int volume=100, int durationMS=0)
Plays the specified samples.
Definition: soundgen.cpp:669
bool play(bool value)
Starts or stops playing.
Definition: soundgen.cpp:644
void clear()
Stops playing and removes all attached waveform generators.
Definition: soundgen.cpp:497
int getSample()
Gets next sample.
Definition: soundgen.cpp:159
void setDutyCycle(int dutyCycle)
Sets square wave duty cycle.
Definition: soundgen.cpp:153
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:144
int getSample()
Gets next sample.
Definition: soundgen.cpp:210
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:202
int getSample()
Gets next sample.
Definition: soundgen.cpp:358
void setFrequency(int value)
Sets output frequency.
Definition: soundgen.cpp:347
int volume()
Determines current volume.
Definition: soundgen.h:134
uint32_t duration()
Returns number of remaining samples to play.
Definition: soundgen.h:95
uint16_t sampleRate()
Determines the sample rate.
Definition: soundgen.h:166
Base abstract class for waveform generators. A waveform generator can be seen as an audio channel tha...
Definition: soundgen.h:70
uint8_t const * data
SoundGenMethod
Specifies sound generation method.
Definition: soundgen.h:328
This file contains all classes related to FabGL Sound System.