arduino-audio-tools
Loading...
Searching...
No Matches
AudioESP32ULP.h
Go to the documentation of this file.
1
10#pragma once
11
12#ifndef ESP32
13#error Only the ESP32 supports ULP audio output
14#endif
15#include "AudioLogger.h"
18#include <driver/dac.h>
19#include <driver/rtc_io.h>
20#include <esp32/ulp.h>
21#include <math.h>
22#include <soc/rtc.h>
23#include "soc/rtc_io_reg.h"
24
25namespace audio_tools {
26
27enum UlpDac { ULP_DAC1 = 1, ULP_DAC2 = 2 };
28
41class AudioESP32ULP : public AudioOutput {
42public:
44 AudioInfo cfg(44100, 2, 16);
45 return cfg;
46 }
47
49 void setMonoDAC(UlpDac dac){
51 }
52
54 void setMinWriteBytes(int bytes){
55 min_write_bytes = bytes;
56 }
57
59 bool begin(AudioInfo info) {
60 TRACEI();
61 cfg = info;
62 stereoOutput = info.channels == 2;
65
66 if (info.bits_per_sample != 16) {
67 LOGE("Unsupported bits_per_sample: %d", info.bits_per_sample);
68 return false;
69 }
70 return setup();
71 }
72
73 size_t write(const uint8_t *data, size_t len) {
74 TRACED();
75 int16_t *data_16 = (int16_t *)data;
76 size_t result = 0;
77 int16_t stereo[2];
78 int frameSize = cfg.channels * sizeof(int16_t);
79 int frames = len / frameSize;
80 for (int j = 0; j < frames; j++) {
81 int pos = j * cfg.channels;
82 stereo[0] = data_16[pos];
83 stereo[1] = stereoOutput ? data_16[pos + 1] : data_16[pos];
84 // blocking write
85 while (!writeFrame(stereo)) {
86 delay(20);
87 }
88 result += frameSize;
89 }
90 return result;
91 }
92
95 return result < min_write_bytes ? 0 : result;
96 }
97
98 void end() {
99 TRACEI();
100 const ulp_insn_t stopulp[] = {// stop the timer
101 I_END(),
102 // end the program
103 I_HALT()};
104
105 size_t load_addr = 0;
106 size_t size = sizeof(stopulp) / sizeof(ulp_insn_t);
108
109 // start
110 ulp_run(0);
111
112 if (activeDACs & 1) {
114 }
115 if (activeDACs & 2) {
117 }
118 }
119
120
121protected:
123 int hertz;
127 bool waitingOddSample = true; // must be set to false for mono output
128 int activeDACs = 3; // 1:DAC1; 2:DAC2; 3:both;
129 bool stereoOutput = true;
130 const int opcodeCount = 20;
131 const uint32_t dacTableStart1 = 2048 - 512;
134 2048 - 512 - 512 - (opcodeCount + 1); // add 512 for mono
138
139 bool setup() {
140 TRACED();
141 if (!stereoOutput) {
142 waitingOddSample = false;
143 // totalSampleWords += 512;
144 // dacTableStart2 = dacTableStart1;
145 }
146
147 // calculate the actual ULP clock
148 unsigned long rtc_8md256_period = rtc_clk_cal(RTC_CAL_8MD256, 1000);
149 unsigned long rtc_fast_freq_hz =
150 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / rtc_8md256_period;
151
152 // initialize DACs
153 if (activeDACs & 1) {
156 }
157 if (activeDACs & 2) {
160 }
161
162 int retAddress1 = 9;
163 int retAddress2 = 14;
164
165 int loopCycles = 134;
166 int loopHalfCycles1 = 90;
167 int loopHalfCycles2 = 44;
168
169 LOGI("Real RTC clock: %d", rtc_fast_freq_hz);
170
172 uint32_t dt2 = 0;
173 if (!stereoOutput) {
176 }
177
178 LOGI("dt: %d", dt);
179 LOGI("dt2: %d", dt2);
180
181 const ulp_insn_t stereo[] = {
182 // reset offset register
183 I_MOVI(R3, 0),
184 // delay to get the right sampling rate
185 I_DELAY(dt), // 6 + dt
186 // reset sample index
187 I_MOVI(R0, 0), // 6
188 // write the index back to memory for the main cpu
189 I_ST(R0, R3, indexAddress), // 8
190 // load the samples
191 I_LD(R1, R0, bufferStart), // 8
192 // mask the lower 8 bits
193 I_ANDI(R2, R1, 0x00ff), // 6
194 // multiply by 2
195 I_LSHI(R2, R2, 1), // 6
196 // add start position
197 I_ADDI(R2, R2, dacTableStart1), // 6
198 // jump to the dac opcode
199 I_BXR(R2), // 4
200 // back from first dac
201 // delay between the two samples in mono rendering
202 I_DELAY(dt2), // 6 + dt2
203 // mask the upper 8 bits
204 I_ANDI(R2, R1, 0xff00), // 6
205 // shift the upper bits to right and multiply by 2
206 I_RSHI(R2, R2, 8 - 1), // 6
207 // add start position of second dac table
208 I_ADDI(R2, R2, dacTableStart2), // 6
209 // jump to the dac opcode
210 I_BXR(R2), // 4
211 // here we get back from writing the second sample
212 // load 0x8080 as sample
213 I_MOVI(R1, 0x8080), // 6
214 // write 0x8080 in the sample buffer
215 I_ST(R1, R0, indexAddress), // 8
216 // increment the sample index
217 I_ADDI(R0, R0, 1), // 6
218 // if reached end of the buffer, jump relative to index reset
219 I_BGE(-16, totalSampleWords), // 4
220 // wait to get the right sample rate (2 cycles more to compensate the
221 // index reset)
222 I_DELAY((unsigned int)dt + 2), // 8 + dt
223 // if not, jump absolute to where index is written to memory
224 I_BXI(3) // 4
225 };
226 // write io and jump back another 12 + 4 + 12 + 4
227
228 size_t load_addr = 0;
229 size_t size = sizeof(stereo) / sizeof(ulp_insn_t);
231 // this is how to get the opcodes
232 // for(int i = 0; i < size; i++)
233 // Serial.println(RTC_SLOW_MEM[i], HEX);
234
235 // create DAC opcode tables
236 switch (activeDACs) {
237 case 1:
238 for (int i = 0; i < 256; i++) {
240 RTC_IO_PAD_DAC1_REG, 19, 26, i); // dac1: 0x1D4C0121 | (i << 10)
241 RTC_SLOW_MEM[dacTableStart1 + 1 + i * 2] =
242 create_I_BXI(retAddress1); // 0x80000000 + retAddress1 * 4
244 RTC_IO_PAD_DAC1_REG, 19, 26, i); // dac2: 0x1D4C0122 | (i << 10)
245 RTC_SLOW_MEM[dacTableStart2 + 1 + i * 2] =
246 create_I_BXI(retAddress2); // 0x80000000 + retAddress2 * 4
247 }
248 break;
249 case 2:
250 for (int i = 0; i < 256; i++) {
252 RTC_IO_PAD_DAC2_REG, 19, 26, i); // dac1: 0x1D4C0121 | (i << 10)
253 RTC_SLOW_MEM[dacTableStart1 + 1 + i * 2] =
254 create_I_BXI(retAddress1); // 0x80000000 + retAddress1 * 4
256 RTC_IO_PAD_DAC2_REG, 19, 26, i); // dac2: 0x1D4C0122 | (i << 10)
257 RTC_SLOW_MEM[dacTableStart2 + 1 + i * 2] =
258 create_I_BXI(retAddress2); // 0x80000000 + retAddress2 * 4
259 }
260 break;
261 case 3:
262 for (int i = 0; i < 256; i++) {
264 RTC_IO_PAD_DAC1_REG, 19, 26, i); // dac1: 0x1D4C0121 | (i << 10)
265 RTC_SLOW_MEM[dacTableStart1 + 1 + i * 2] =
266 create_I_BXI(retAddress1); // 0x80000000 + retAddress1 * 4
268 RTC_IO_PAD_DAC1_REG, 19, 26, i); // dac2: 0x1D4C0122 | (i << 10)
269 RTC_SLOW_MEM[dacTableStart2 + 1 + i * 2] =
270 create_I_BXI(retAddress2); // 0x80000000 + retAddress2 * 4
271 }
272 break;
273 }
274
275 // set all samples to 128 (silence)
276 for (int i = 0; i < totalSampleWords; i++)
277 RTC_SLOW_MEM[bufferStart + i] = 0x8080;
278
279 // start
281 ulp_run(0);
282
283 // wait until ULP starts using samples and the index of output sample
284 // advances
285 while (RTC_SLOW_MEM[indexAddress] == 0)
286 delay(1);
287
288 return true;
289 }
290
291 bool writeFrame(int16_t sample[2]) {
292 TRACED();
293 int16_t ms[2];
294 ms[0] = sample[0];
295 ms[1] = sample[1];
296
297 // TODO: needs improvement (counting is different here with respect to ULP
298 // code)
299 int currentSample = RTC_SLOW_MEM[indexAddress] & 0xffff;
300 int currentWord = currentSample >> 1;
301
302 for (int i = 0; i < 2; i++) {
303 ms[i] = ((ms[i] >> 8) + 128) & 0xff;
304 }
305 if (!stereoOutput) // mix both channels
306 ms[0] =
307 (uint16_t)(((uint32_t)((int32_t)(ms[0]) + (int32_t)(ms[1])) >> 1) &
308 0xff);
309
310 if (waitingOddSample) { // always true for stereo because samples are
311 // consumed in pairs
312 if (lastFilledWord !=
313 currentWord) // accept sample if writing index lastFilledWord has not
314 // reached index of output sample
315 {
316 unsigned int w;
317 if (stereoOutput) {
318 w = ms[0];
319 w |= ms[1] << 8;
320 } else {
322 w |= ms[0] << 8;
323 bufferedOddSample = 128;
324 waitingOddSample = false;
325 }
329 lastFilledWord = 0;
330 return true;
331 } else {
332 return false;
333 }
334 } else {
335 bufferedOddSample = ms[0];
336 waitingOddSample = true;
337 return true;
338 }
339 }
340
353
355 typedef union {
358 } ulp_union;
361 recover_ins.ulp_ins = singleinstruction[0];
362 return (uint32_t)(recover_ins.ulp_bin);
363 }
364};
365
366}
#define TRACEI()
Definition AudioLoggerIDF.h:32
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGE(...)
Definition AudioLoggerIDF.h:30
Outputs to ESP32 DAC through the ULP (Ultra> Low Power coprocessor), freeing I2S for other uses....
Definition AudioESP32ULP.h:41
uint32_t totalSampleWords
Definition AudioESP32ULP.h:133
int lastFilledWord
Definition AudioESP32ULP.h:122
const uint32_t bufferStart
Definition AudioESP32ULP.h:137
UlpDac selected_mono_dac
Definition AudioESP32ULP.h:125
const int totalSamples
Definition AudioESP32ULP.h:135
int activeDACs
Definition AudioESP32ULP.h:128
void setMonoDAC(UlpDac dac)
Selects the DAC when we have a mono signal.
Definition AudioESP32ULP.h:49
uint32_t create_I_WR_REG(uint32_t reg, uint32_t low_bit, uint32_t high_bit, uint32_t val)
Definition AudioESP32ULP.h:341
int min_write_bytes
Definition AudioESP32ULP.h:124
uint32_t create_I_BXI(uint32_t imm_pc)
Definition AudioESP32ULP.h:354
uint8_t bufferedOddSample
Definition AudioESP32ULP.h:126
AudioInfo defaultConfig()
Definition AudioESP32ULP.h:43
int availableForWrite()
Definition AudioESP32ULP.h:93
const uint32_t dacTableStart1
Definition AudioESP32ULP.h:131
int hertz
Definition AudioESP32ULP.h:123
void setMinWriteBytes(int bytes)
Selects the limit for the availableForWrite to report the data.
Definition AudioESP32ULP.h:54
void end()
Definition AudioESP32ULP.h:98
const int opcodeCount
Definition AudioESP32ULP.h:130
const uint32_t indexAddress
Definition AudioESP32ULP.h:136
const uint32_t dacTableStart2
Definition AudioESP32ULP.h:132
bool waitingOddSample
Definition AudioESP32ULP.h:127
bool stereoOutput
Definition AudioESP32ULP.h:129
bool begin(AudioInfo info)
Starts the processing. I the output is mono, we can determine the output pin by selecting DAC1 (gpio2...
Definition AudioESP32ULP.h:59
bool setup()
Definition AudioESP32ULP.h:139
bool writeFrame(int16_t sample[2])
Definition AudioESP32ULP.h:291
size_t write(const uint8_t *data, size_t len)
Definition AudioESP32ULP.h:73
Abstract Audio Ouptut class.
Definition AudioOutput.h:25
AudioInfo cfg
Definition AudioOutput.h:88
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
void delay(uint32_t ms)
Definition Arduino.h:255
UlpDac
Definition AudioESP32ULP.h:27
@ ULP_DAC1
Definition AudioESP32ULP.h:27
@ ULP_DAC2
Definition AudioESP32ULP.h:27
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:508
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:51
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:57