Skip to content

Commit 1565ee2

Browse files
committed
SPDIFStream support of print output
1 parent 6b8962a commit 1565ee2

File tree

1 file changed

+173
-94
lines changed

1 file changed

+173
-94
lines changed

src/AudioExperiments/AudioSPDIF.h

Lines changed: 173 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,132 @@ static uint32_t *spdif_ptr;
111111
* @copyright GPLv3
112112
*/
113113
struct SPDIFConfig : public AudioBaseInfo {
114-
SPDIFConfig(){
114+
SPDIFConfig() {
115115
bits_per_sample = 16;
116116
channels = 2;
117117
}
118118
int port_no = 0; // processor dependent port
119119
int pin_data = SPDIF_DATA_PIN;
120120
};
121121

122+
/**
123+
* @brief Interface definition for SPDIF output class
124+
*
125+
*/
126+
class SPDIFOut {
127+
public:
128+
virtual void begin(SPDIFConfig &cfg);
129+
virtual bool end();
130+
virtual size_t write(uint8_t *spdif_buf, size_t len);
131+
};
132+
133+
/**
134+
* @brief Generic I2S Output
135+
*
136+
*/
137+
class SPDIFOutI2S : public SPDIFOut {
138+
public:
139+
SPDIFOutI2S() = default;
140+
141+
void begin(SPDIFConfig &cfg) {
142+
int sample_rate = cfg.sample_rate * BMC_BITS_FACTOR;
143+
int bclk = sample_rate * I2S_BITS_PER_SAMPLE * I2S_CHANNELS;
144+
int mclk = (I2S_BUG_MAGIC / bclk) * bclk; // use mclk for avoiding I2S bug
145+
146+
I2SConfig i2s_cfg;
147+
i2s_cfg.sample_rate = sample_rate;
148+
i2s_cfg.channels = cfg.channels;
149+
i2s_cfg.bits_per_sample = 32;
150+
i2s_cfg.pin_ws = -1;
151+
i2s_cfg.pin_bck = -1;
152+
i2s_cfg.pin_data = cfg.pin_data;
153+
i2s_cfg.use_apll = true;
154+
i2s_cfg.fixed_mclk = mclk;
155+
156+
i2s.begin(i2s_cfg);
157+
}
158+
159+
bool end() {
160+
i2s.end();
161+
return true;
162+
}
163+
164+
size_t write(uint8_t *spdif_buf, size_t len) {
165+
return i2s.write(spdif_buf, len);
166+
}
167+
168+
protected:
169+
I2SStream i2s;
170+
};
171+
172+
/**
173+
* @brief ESP32 specific Output
174+
*
175+
*/
176+
class SPDFOutI2SESP32 : public SPDIFOut {
177+
public:
178+
SPDFOutI2SESP32() = default;
179+
180+
// initialize I2S for S/PDIF transmission
181+
void begin(SPDIFConfig &cfg) {
182+
// uninstall and reinstall I2S driver for avoiding I2S bug
183+
int sample_rate = cfg.sample_rate * BMC_BITS_FACTOR;
184+
int bclk = sample_rate * I2S_BITS_PER_SAMPLE * I2S_CHANNELS;
185+
int mclk = (I2S_BUG_MAGIC / bclk) * bclk; // use mclk for avoiding I2S bug
186+
187+
i2s_config_t i2s_config = {
188+
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
189+
.sample_rate = sample_rate,
190+
.bits_per_sample = (i2s_bits_per_sample_t)I2S_BITS_PER_SAMPLE,
191+
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
192+
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S,
193+
.intr_alloc_flags = 0,
194+
.dma_buf_count = DMA_BUF_COUNT,
195+
.dma_buf_len = DMA_BUF_LEN,
196+
.use_apll = true,
197+
.tx_desc_auto_clear = true,
198+
.fixed_mclk = mclk, // avoiding I2S bug
199+
};
200+
i2s_pin_config_t pin_config = {
201+
.bck_io_num = -1,
202+
.ws_io_num = -1,
203+
.data_out_num = cfg.pin_data,
204+
.data_in_num = -1,
205+
};
206+
207+
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL));
208+
ESP_ERROR_CHECK(i2s_set_pin(I2S_NUM, &pin_config));
209+
}
210+
211+
bool end() {
212+
return i2s_driver_uninstall(I2S_NUM) == ESP_OK;
213+
}
214+
215+
size_t write(uint8_t *spdif_buf, size_t len) {
216+
size_t i2s_write_len;
217+
i2s_write(I2S_NUM, spdif_buf, len, &i2s_write_len, portMAX_DELAY);
218+
return i2s_write_len;
219+
}
220+
};
221+
222+
/**
223+
* @brief Generic output of SPDIF to Arduino Stream
224+
*
225+
*/
226+
class SPDIFOutGeneric : public SPDIFOut {
227+
public:
228+
SPDIFOutGeneric(Print &out) { this->out = &out; }
229+
virtual void begin(SPDIFConfig &cfg) { }
230+
virtual bool end() {return true;}
231+
232+
virtual size_t write(uint8_t *data, size_t len) {
233+
return out->write(data, len);
234+
}
235+
236+
protected:
237+
Print *out = nullptr;
238+
};
239+
122240
/**
123241
* @brief Output as 16 bit stereo SPDIF on the I2S data output pin
124242
* @author Phil Schatzmann
@@ -127,22 +245,51 @@ struct SPDIFConfig : public AudioBaseInfo {
127245
*/
128246
class SPDIFStream16Bit2Channels : public AudioStreamX {
129247
public:
248+
/// default constructor
130249
SPDIFStream16Bit2Channels() = default;
131250

251+
/// destructor
252+
~SPDIFStream16Bit2Channels() { end(); }
253+
132254
/// Starting with default settings
133255
bool begin() { return begin(defaultConfig()); }
134256

135257
/// Start with the provided parameters
136258
bool begin(SPDIFConfig cfg) {
259+
if (out == nullptr) {
260+
#if USE_ESP32_I2S == 1
261+
out = new SPDFOutI2SESP32();
262+
#else
263+
out = new SPDIFOutI2S();
264+
#endif
265+
}
266+
if (i2sOn){
267+
out->end();
268+
}
269+
137270
// initialize S/PDIF buffer
138271
spdif_buf_init();
139272
spdif_ptr = spdif_buf;
273+
out->begin(cfg);
140274

141-
spdif_init(cfg);
142275
i2sOn = true;
143276
return true;
144277
}
145278

279+
bool end() {
280+
bool result = true;
281+
if (out != nullptr) {
282+
result = out->end();
283+
delete out;
284+
out = nullptr;
285+
}
286+
i2sOn = false;
287+
return result;
288+
}
289+
290+
/// Defines the Output
291+
void setOutput(SPDIFOut *out_ptr) { this->out = out_ptr; }
292+
146293
/// Change the audio parameters
147294
virtual void setAudioInfo(AudioBaseInfo info) {
148295
cfg.bits_per_sample = info.bits_per_sample;
@@ -153,8 +300,7 @@ class SPDIFStream16Bit2Channels : public AudioStreamX {
153300
info.bits_per_sample);
154301
}
155302
if (info.channels != 2) {
156-
LOGE("Unsupported number of channels: %d - must be 2!",
157-
info.channels);
303+
LOGE("Unsupported number of channels: %d - must be 2!", info.channels);
158304
}
159305
begin(cfg);
160306
}
@@ -184,37 +330,18 @@ class SPDIFStream16Bit2Channels : public AudioStreamX {
184330
// set block start preamble
185331
((uint8_t *)spdif_buf)[SYNC_OFFSET] ^= SYNC_FLIP;
186332

187-
result += spdif_write((uint8_t *)spdif_buf, sizeof(spdif_buf));
333+
result += out->write((uint8_t *)spdif_buf, sizeof(spdif_buf));
188334
spdif_ptr = spdif_buf;
189335
}
190336
}
191337

192338
return result / 2;
193339
}
194340

195-
#if USE_ESP32_I2S == 1
196-
197-
bool end() {
198-
i2sOn = false;
199-
return i2s_driver_uninstall(I2S_NUM) == ESP_OK;
200-
}
201-
202-
#else
203-
204-
bool end() {
205-
i2s.end();
206-
i2sOn = false;
207-
return true;
208-
}
209-
210-
#endif
211-
212341
protected:
213-
bool i2sOn;
342+
bool i2sOn = false;
214343
SPDIFConfig cfg;
215-
#if USE_ESP32_I2S != 1
216-
I2SStream i2s;
217-
#endif
344+
SPDIFOut *out = nullptr;
218345

219346
// initialize S/PDIF buffer
220347
static void spdif_buf_init(void) {
@@ -225,83 +352,34 @@ class SPDIFStream16Bit2Channels : public AudioStreamX {
225352
spdif_buf[i] = bmc_mw ^= BMC_MW_DIF;
226353
}
227354
}
228-
229-
#if USE_ESP32_I2S == 1
230-
// initialize I2S for S/PDIF transmission
231-
void spdif_init(SPDIFConfig &cfg) {
232-
// uninstall and reinstall I2S driver for avoiding I2S bug
233-
if (i2sOn) {
234-
end();
235-
}
236-
int sample_rate = cfg.sample_rate * BMC_BITS_FACTOR;
237-
int bclk = sample_rate * I2S_BITS_PER_SAMPLE * I2S_CHANNELS;
238-
int mclk = (I2S_BUG_MAGIC / bclk) * bclk; // use mclk for avoiding I2S bug
239-
240-
i2s_config_t i2s_config = {
241-
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
242-
.sample_rate = sample_rate,
243-
.bits_per_sample = (i2s_bits_per_sample_t)I2S_BITS_PER_SAMPLE,
244-
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
245-
.communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S,
246-
.intr_alloc_flags = 0,
247-
.dma_buf_count = DMA_BUF_COUNT,
248-
.dma_buf_len = DMA_BUF_LEN,
249-
.use_apll = true,
250-
.tx_desc_auto_clear = true,
251-
.fixed_mclk = mclk, // avoiding I2S bug
252-
};
253-
i2s_pin_config_t pin_config = {
254-
.bck_io_num = -1,
255-
.ws_io_num = -1,
256-
.data_out_num = cfg.pin_data,
257-
.data_in_num = -1,
258-
};
259-
260-
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL));
261-
ESP_ERROR_CHECK(i2s_set_pin(I2S_NUM, &pin_config));
262-
}
263-
264-
size_t spdif_write(uint8_t *spdif_buf, size_t len) {
265-
size_t i2s_write_len;
266-
i2s_write(I2S_NUM, spdif_buf, len, &i2s_write_len, portMAX_DELAY);
267-
return i2s_write_len;
268-
}
269-
270-
#else
271-
void spdif_init(SPDIFConfig &cfg) {
272-
int sample_rate = cfg.sample_rate * BMC_BITS_FACTOR;
273-
int bclk = sample_rate * I2S_BITS_PER_SAMPLE * I2S_CHANNELS;
274-
int mclk = (I2S_BUG_MAGIC / bclk) * bclk; // use mclk for avoiding I2S bug
275-
276-
I2SConfig i2s_cfg;
277-
i2s_cfg.sample_rate = sample_rate;
278-
i2s_cfg.channels = cfg.channels;
279-
i2s_cfg.bits_per_sample = 32;
280-
i2s_cfg.pin_ws = -1;
281-
i2s_cfg.pin_bck = -1;
282-
i2s_cfg.pin_data = cfg.pin_data;
283-
i2s_cfg.use_apll = true;
284-
i2s_cfg.fixed_mclk = mclk;
285-
286-
i2s.begin(i2s_cfg);
287-
}
288-
289-
size_t spdif_write(uint8_t *spdif_buf, size_t len) {
290-
return i2s.write(spdif_buf, len);
291-
}
292-
293-
#endif
294355
};
295356

296357
/**
297358
* @brief SPDIF Stream
298-
* We only support the output of different bits_per_sample and mono output
359+
* We support the output of different bits_per_sample and mono output
299360
* @author Phil Schatzmann
300361
* @copyright GPLv3
301362
*/
302363
class SPDIFStream : public AudioStreamX {
303364
public:
365+
/// Default Constructor
304366
SPDIFStream() = default;
367+
368+
/// Constructor which is defining a specific output class
369+
SPDIFStream(Print &out){
370+
spdif_out = new SPDIFOutGeneric(out);
371+
spdif.setOutput(spdif_out);
372+
}
373+
374+
///Destructor
375+
~SPDIFStream() {
376+
end();
377+
if (spdif_out!=nullptr){
378+
delete spdif_out;
379+
spdif_out = nullptr;
380+
}
381+
}
382+
305383
/// start SPDIF with default configuration
306384
bool begin() { spdif.begin(); }
307385
/// start SPDIF with the indicated configuration
@@ -346,7 +424,8 @@ class SPDIFStream : public AudioStreamX {
346424
protected:
347425
SPDIFConfig cfg;
348426
SPDIFStream16Bit2Channels spdif;
349-
FormatConverterStream converter {spdif};
427+
FormatConverterStream converter{spdif};
428+
SPDIFOutGeneric *spdif_out=nullptr;
350429
};
351430

352431
} // namespace audio_tools

0 commit comments

Comments
 (0)