Mozzi  version v2.0
sound synthesis library for Arduino
MozziGuts_impl_ESP8266.hpp
1 /*
2  * MozziGuts_impl_ESP8266.hpp
3  *
4  * This file is part of Mozzi.
5  *
6  * Copyright 2020-2024 Thomas Friedrichsmeier and the Mozzi Team
7  *
8  * Mozzi is licensed under the GNU Lesser General Public Licence (LGPL) Version 2.1 or later.
9  *
10 */
11 
12 #if !(IS_ESP8266())
13 # error "Wrong implementation included for this platform"
14 #endif
15 
16 namespace MozziPrivate {
17 
18 ////// BEGIN analog input code ////////
19 #if MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_STANDARD)
20 #error not yet implemented
21 
22 #define getADCReading() 0
23 #define channelNumToIndex(channel) channel
25  return pin;
26 }
28 }
30 }
32 }
34 }
35 #endif
36 ////// END analog input code ////////
37 
38 //// BEGIN AUDIO OUTPUT code ///////
39 #define LOOP_YIELD yield();
40 
41 } // namespace MozziPrivate
42 #include <uart.h>
43 #include <I2S.h>
44 namespace MozziPrivate {
45 uint16_t output_buffer_size = 0;
46 
47 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_PDM_VIA_SERIAL, MOZZI_OUTPUT_I2S_DAC) // i.e. not external
48 
49 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
50 } // namespace MozziPrivate
51 # include <i2s.h>
52 namespace MozziPrivate {
53 inline bool canBufferAudioOutput() {
54  return (i2s_available() >= MOZZI_PDM_RESOLUTION);
55 }
56 inline void audioOutput(const AudioOutput f) {
57  for (uint8_t words = 0; words < MOZZI_PDM_RESOLUTION; ++words) {
59  }
60 }
61 # elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC)
62 } // namespace MozziPrivate
63 # include <i2s.h>
64 namespace MozziPrivate {
65 inline bool canBufferAudioOutput() {
66  return (i2s_available() >= MOZZI_PDM_RESOLUTION);
67 }
68 inline void audioOutput(const AudioOutput f) {
69  i2s_write_lr(f.l(), f.r()); // Note: i2s_write expects zero-centered output
70 }
71 # else
73 // NOTE: This intermediate step is needed because the output timer is running at a rate higher than MOZZI_AUDIO_RATE, and we need to rely on the (tiny)
74 // serial buffer itself to achieve appropriate rate control
76  // Note: That unreadble mess is an optimized version of Serial1.availableForWrite()
77  while ((UART_TX_FIFO_SIZE - ((U1S >> USTXC) & 0xff)) > (MOZZI_PDM_RESOLUTION * 4)) {
79  }
80 }
81 
82 inline void audioOutput(const AudioOutput f) {
83  // optimized version of: Serial1.write(...);
84  for (uint8_t i = 0; i < MOZZI_PDM_RESOLUTION*4; ++i) {
86  }
87 }
88 # endif
89 #endif
90 
91 static void startAudio() {
92 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED) // for external audio output, set up a timer running a audio rate
93  timer1_isr_init();
94  timer1_attachInterrupt(defaultAudioOutput);
95  timer1_enable(TIM_DIV1, TIM_EDGE, TIM_LOOP);
96  timer1_write(F_CPU / MOZZI_AUDIO_RATE);
97 #elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_SERIAL)
98  Serial1.begin(
99  MOZZI_AUDIO_RATE * (MOZZI_PDM_RESOLUTION * 40), SERIAL_8N1,
100  SERIAL_TX_ONLY); // Note: PDM_RESOLUTION corresponds to packets of 32
101  // encoded bits However, the UART (unfortunately) adds a
102  // start and stop bit each around each byte, thus sending
103  // a total to 40 bits per audio sample per
104  // PDM_RESOLUTION.
105  // set up a timer to copy from Mozzi output_buffer into Serial TX buffer
106  timer1_isr_init();
107  timer1_attachInterrupt(esp8266_serial_audio_output);
108  // UART FIFO buffer size is 128 bytes. To be on the safe side, we keep the
109  // interval to the time needed to write half of that. PDM_RESOLUTION * 4 bytes
110  // per sample written.
111  timer1_enable(TIM_DIV16, TIM_EDGE, TIM_LOOP);
112  timer1_write(F_CPU / (MOZZI_AUDIO_RATE * MOZZI_PDM_RESOLUTION));
113 #else
114  i2s_begin();
115 # if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S)
116  pinMode(2, INPUT); // Set the two unneeded I2S pins to input mode, to reduce
117  // side effects
118  pinMode(15, INPUT);
119 # endif
120  i2s_set_rate(MOZZI_AUDIO_RATE * MOZZI_PDM_RESOLUTION);
121  if (output_buffer_size == 0)
122  output_buffer_size =
123  i2s_available(); // Do not reset count when stopping / restarting
124 #endif
125 }
126 
127 
128 void stopMozzi() {
129 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S, MOZZI_OUTPUT_I2S_DAC)
130  i2s_end();
131 #elif MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_SERIAL, MOZZI_OUTPUT_EXTERNAL_TIMED)
132  timer1_disable();
133 #endif
134  interrupts();
135 }
136 
137 #if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_PDM_VIA_I2S) && (MOZZI_PDM_RESOLUTION != 1)
138 # define AUDIOTICK_ADJUSTMENT ((output_buffer_size - i2s_available()) / MOZZI_PDM_RESOLUTION)
139 #else
140 # define AUDIOTICK_ADJUSTMENT (output_buffer_size - i2s_available())
141 #endif
142 
143 //// END AUDIO OUTPUT code ///////
144 
145 //// BEGIN Random seeding ////////
146 } //namespace MozziPrivate
147 #include <esp8266_peri.h>
148 namespace MozziPrivate {
149 void MozziRandPrivate::autoSeed() {
150  x = RANDOM_REG32;
151  // TODO: The XORs may not be needed, but for lack of documentation (that I could find), let's assume RANDOM_REG32
152  // itself might not get updated on every read. NOTE: x, y, and z are initialized to non-zero, before this.
153  y = y ^ RANDOM_REG32;
154  z = z ^ RANDOM_REG32;
155 }
156 //// END Random seeding ////////
157 } // namespace MozziPrivate