Skip to content

Commit 7d6c3b4

Browse files
committed
Compile Errors for STKEffects.h
1 parent 9e88848 commit 7d6c3b4

File tree

1 file changed

+275
-0
lines changed

1 file changed

+275
-0
lines changed

src/AudioExperiments/BitBangI2S.h

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
#pragma once
2+
3+
/**
4+
* @brief A 2 Channel Audio Frame
5+
*
6+
*/
7+
8+
class I2SOut;
9+
void *p_I2SOut=nullptr;
10+
11+
12+
class BitBangI2SBase {
13+
public:
14+
BitBangI2SBase(){
15+
}
16+
17+
virtual void begin(I2SConfig cfg) {
18+
this->cfg = cfg;
19+
active = true;
20+
}
21+
22+
/**
23+
* @brief Stops the processing
24+
*
25+
*/
26+
void end() {
27+
active = false;
28+
}
29+
30+
/**
31+
* @brief Write some audio data
32+
*
33+
* @param data
34+
* @param len
35+
* @return size_t
36+
*/
37+
size_t write(void* data, size_t len){
38+
return buffer.writeArray(data, len);
39+
}
40+
41+
operator bool() {
42+
return active;
43+
}
44+
45+
protected:
46+
I2SConfig cfg;
47+
bool active = false;
48+
RingBuffer<uint8_t> buffer(1024);
49+
50+
void writePins(int16_t data, int8_t bit, bool rlValue){
51+
bool value = data >> (15 - bit) & 1;
52+
fastWrite(value, true, rlValue)
53+
delayMicroseconds(bitTimeUs/2);
54+
fastWrite(value, false, rlValue)
55+
delayMicroseconds(bitTimeUs/2);
56+
}
57+
58+
}
59+
60+
/**
61+
* @brief Some Processors have multiple cores. We use one core to bit bang the I2S functioinality
62+
* just using basic functionality like loops. This is a base abstract class which needs to implement
63+
* the processor specific functionality.
64+
*/
65+
class BitBangI2SToCore : public BitBangI2SBase {
66+
public:
67+
BitBangI2SToCore() {
68+
p_I2SOut = this;
69+
}
70+
71+
/**
72+
* @brief Starts the I2S processing
73+
*
74+
* @param cfg
75+
*/
76+
virtual void begin(I2SConfig cfg) {
77+
this->cfg = cfg;
78+
79+
// calculate timings
80+
bitRate = cfg.sampleRate * cfg.bits_per_sample * 2;
81+
bitTimeUs = (1000000 / bitRate) * 2;
82+
size_t writeTimeUs = measureWriteTimes(1000) / 1000;
83+
// adjust Waiting time by time spend for write
84+
bitTimeUs -= writeTimeUs;
85+
86+
if (bitTimeUs<0){
87+
LOGE("bitrate is too high - we use maximum possible value")
88+
bitTimeUs = 0;
89+
}
90+
91+
startCore(runLoop);
92+
active = true;
93+
94+
}
95+
96+
protected:
97+
int bitRate;
98+
int bitTimeUs;
99+
100+
/**
101+
* @brief Starts the loop on a separate core
102+
*
103+
* @param runLoop
104+
*/
105+
virtual void startCore(void(*runLoop)()) = 0
106+
107+
/**
108+
* @brief Fast output to the 3 I2S pins in one go
109+
*
110+
* @param value
111+
* @param bitClockValue
112+
* @param rlValue
113+
*/
114+
virtual void fastWrite(bool value, bool bitClockValue, bool rlValue) = 0;
115+
116+
117+
/// mesures the pin write times
118+
size_t measureWriteTimes(size_t count){
119+
size_t start = micros();
120+
for (size_t j=0;j<count;j++){
121+
fastWrite(false, false, false);
122+
fastWrite(true, true, true);
123+
}
124+
return micros()-start();
125+
}
126+
127+
/// Process output in endless loop
128+
static void runLoop() {
129+
I2SOutCore1 ptr = (I2SOutCore1*)p_I2SOut;
130+
while(ptr->active){
131+
uint8_t audio[cfg.bits_per_sample][2];
132+
int byte_count = cfg.bits_per_sample*2;
133+
if (ptr->buffer.available() >= byte_count){
134+
ptr->buffer.readArray((uint8_t*)audio, byte_count);
135+
} else {
136+
memset(audio,0,byte_count);
137+
}
138+
for (uint8_t ch=0; ch<2; ch++){
139+
for (uint8_t j=0;j<cfg.bits_per_sample;j++){
140+
ptr->writePins(audio[ch], j, ch);
141+
}
142+
}
143+
}
144+
}
145+
};
146+
147+
/**
148+
* @brief Bit Banging I2S using a timer
149+
*/
150+
151+
class BitBangI2SOutTimer : public BitBangI2SBase {
152+
public:
153+
BitBangI2SOutTimer() : BitBangI2SBase() {
154+
p_I2SOutTimer = this;
155+
}
156+
157+
virtual void begin(I2SConfig cfg) {
158+
this->cfg = cfg;
159+
this->getData = getData;
160+
startTimer(bitTimeUs/2);
161+
active = true;
162+
}
163+
164+
protected:
165+
uint8_t counter = 0;
166+
uint8_t bit_counter = 0;
167+
bool bit_value = false;
168+
uint8_t audio[self->cfg.bits_per_sample][2];
169+
int byte_count = self->cfg.bits_per_sample*2;
170+
171+
172+
virtual void startTimer(int time) = 0;
173+
174+
virtual void fastWrite(bool value, bool bitClockValue, bool rlValue) = 0;
175+
176+
/**
177+
* @brief The timer is called at the bitrate*2 in order to switch the bit_value on and
178+
* at the next call off again.
179+
*
180+
* @param ptr
181+
*/
182+
static void repeating_timer_callback(void *ptr) {
183+
I2SOutTimer *self =(I2SOutTimer *)ptr;
184+
185+
// bit value is on
186+
if (bit_value) {
187+
if (counter==cfg.bits_per_sample*2){
188+
// we need to switch the lr channel and get more data
189+
if (self->buffer.available() >= byte_count){
190+
self->buffer.readArray((uint8_t*)audio, byte_count);
191+
} else {
192+
memset(audio,0,byte_count);
193+
}
194+
}
195+
if (bit_counter==16){
196+
bit_counter = 0;
197+
}
198+
} else {
199+
// bit value is off
200+
}
201+
202+
self->fastWrite(audio[bit_value], bit_value, lr_value);
203+
204+
// toggle bit value
205+
bit_value = !bit_value;
206+
207+
if (bit_value) {
208+
counter++;
209+
bit_counter++;
210+
lr_value = !lr_value;
211+
}
212+
}
213+
214+
};
215+
216+
217+
/**
218+
* @brief RP2040 implementation of BitBangI2SToCore
219+
*
220+
*/
221+
class RP2040I2SOutCore1 : public BitBangI2SToCore {
222+
public:
223+
RP2040I2SOutCore1() {
224+
}
225+
226+
virtual void begin(I2SConfig cfg) {
227+
this->cfg = cfg;
228+
mask &= 1<<cfg.dataPin;
229+
mask &= 1<<cfg.rlClockPin;
230+
mask &= 1<<cfg.bitClockPin;
231+
}
232+
233+
protected:
234+
uint32_t mask=0;
235+
236+
void startCore(void(*runLoop)()) override {
237+
multicore_launch_core1(runLoop);
238+
}
239+
240+
void fastWrite(bool value, bool bitClockValue, bool rlValue) override {
241+
uint32_t value = static_cast<uint32_t<(value)<<dataPin
242+
& static_cast<uint32_t<(rlValue)<<lrClockPin
243+
& static_cast<uint32_t<(bitClockValue)<<bitClockPin
244+
gpio_put_masked(mask, value);
245+
}
246+
}
247+
248+
/**
249+
* @brief RP2040 implementation of BitBangI2SOutTimer
250+
*
251+
*/
252+
class RP2040BitBangI2SOutTimer : public BitBangI2SOutTimer {
253+
public:
254+
virtual void begin(I2SConfig cfg) {
255+
this->cfg = cfg;
256+
mask &= 1<<cfg.dataPin;
257+
mask &= 1<<cfg.rlClockPin;
258+
mask &= 1<<cfg.bitClockPin;
259+
}
260+
261+
protected:
262+
uint32_t mask=0;
263+
struct repeating_timer timer;
264+
265+
virtual void startTimer(int timeUs) override {
266+
add_repeating_timer_us(timeUs, repeating_timer_callback, this, &timer);
267+
}
268+
269+
void fastWrite(bool value, bool bitClockValue, bool rlValue) override {
270+
uint32_t value = static_cast<uint32_t<(value)<<dataPin
271+
& static_cast<uint32_t<(rlValue)<<lrClockPin
272+
& static_cast<uint32_t<(bitClockValue)<<bitClockPin
273+
gpio_put_masked(mask, value);
274+
}
275+
};

0 commit comments

Comments
 (0)