arduino-audio-tools
Loading...
Searching...
No Matches
SPDIFOutput.h
Go to the documentation of this file.
1
18#pragma once
19
20#include "AudioToolsConfig.h"
25
26// Default Data Pin
27#ifndef SPDIF_DATA_PIN
28#define SPDIF_DATA_PIN 23
29#endif
30
31#define I2S_NUM ((i2s_port_t)0)
32#define I2S_BITS_PER_SAMPLE (32)
33#define I2S_CHANNELS 2
34#define BMC_BITS_PER_SAMPLE 64
35#define BMC_BITS_FACTOR (BMC_BITS_PER_SAMPLE / I2S_BITS_PER_SAMPLE)
36#define SPDIF_BLOCK_SAMPLES 192
37#define SPDIF_BUF_DIV 2 // double buffering
38#define DMA_BUF_COUNT 2
39#define DMA_BUF_LEN \
40 (SPDIF_BLOCK_SAMPLES * BMC_BITS_PER_SAMPLE / I2S_BITS_PER_SAMPLE / \
41 SPDIF_BUF_DIV)
42#define I2S_BUG_MAGIC (26 * 1000 * 1000) // magic number for avoiding I2S bug
43#define SPDIF_BLOCK_SIZE \
44 (SPDIF_BLOCK_SAMPLES * (BMC_BITS_PER_SAMPLE / 8) * I2S_CHANNELS)
45#define SPDIF_BUF_SIZE (SPDIF_BLOCK_SIZE / SPDIF_BUF_DIV)
46#define SPDIF_BUF_ARRAY_SIZE (SPDIF_BUF_SIZE / sizeof(uint32_t))
47
48// BMC preamble
49#define BMC_B 0x33173333 // block start
50#define BMC_M 0x331d3333 // left ch
51#define BMC_W 0x331b3333 // right ch
52#define BMC_MW_DIF (BMC_M ^ BMC_W)
53#define SYNC_OFFSET 2 // byte offset of SYNC
54#define SYNC_FLIP ((BMC_B ^ BMC_M) >> (SYNC_OFFSET * 8))
55
56
57namespace audio_tools {
58
59/*
60 * 8bit PCM to 16bit BMC conversion table, LSb first, 1 end
61 */
62static const uint16_t bmc_tab_uint[256] = {
63 0x3333, 0xb333, 0xd333, 0x5333, 0xcb33, 0x4b33, 0x2b33, 0xab33, 0xcd33,
64 0x4d33, 0x2d33, 0xad33, 0x3533, 0xb533, 0xd533, 0x5533, 0xccb3, 0x4cb3,
65 0x2cb3, 0xacb3, 0x34b3, 0xb4b3, 0xd4b3, 0x54b3, 0x32b3, 0xb2b3, 0xd2b3,
66 0x52b3, 0xcab3, 0x4ab3, 0x2ab3, 0xaab3, 0xccd3, 0x4cd3, 0x2cd3, 0xacd3,
67 0x34d3, 0xb4d3, 0xd4d3, 0x54d3, 0x32d3, 0xb2d3, 0xd2d3, 0x52d3, 0xcad3,
68 0x4ad3, 0x2ad3, 0xaad3, 0x3353, 0xb353, 0xd353, 0x5353, 0xcb53, 0x4b53,
69 0x2b53, 0xab53, 0xcd53, 0x4d53, 0x2d53, 0xad53, 0x3553, 0xb553, 0xd553,
70 0x5553, 0xcccb, 0x4ccb, 0x2ccb, 0xaccb, 0x34cb, 0xb4cb, 0xd4cb, 0x54cb,
71 0x32cb, 0xb2cb, 0xd2cb, 0x52cb, 0xcacb, 0x4acb, 0x2acb, 0xaacb, 0x334b,
72 0xb34b, 0xd34b, 0x534b, 0xcb4b, 0x4b4b, 0x2b4b, 0xab4b, 0xcd4b, 0x4d4b,
73 0x2d4b, 0xad4b, 0x354b, 0xb54b, 0xd54b, 0x554b, 0x332b, 0xb32b, 0xd32b,
74 0x532b, 0xcb2b, 0x4b2b, 0x2b2b, 0xab2b, 0xcd2b, 0x4d2b, 0x2d2b, 0xad2b,
75 0x352b, 0xb52b, 0xd52b, 0x552b, 0xccab, 0x4cab, 0x2cab, 0xacab, 0x34ab,
76 0xb4ab, 0xd4ab, 0x54ab, 0x32ab, 0xb2ab, 0xd2ab, 0x52ab, 0xcaab, 0x4aab,
77 0x2aab, 0xaaab, 0xcccd, 0x4ccd, 0x2ccd, 0xaccd, 0x34cd, 0xb4cd, 0xd4cd,
78 0x54cd, 0x32cd, 0xb2cd, 0xd2cd, 0x52cd, 0xcacd, 0x4acd, 0x2acd, 0xaacd,
79 0x334d, 0xb34d, 0xd34d, 0x534d, 0xcb4d, 0x4b4d, 0x2b4d, 0xab4d, 0xcd4d,
80 0x4d4d, 0x2d4d, 0xad4d, 0x354d, 0xb54d, 0xd54d, 0x554d, 0x332d, 0xb32d,
81 0xd32d, 0x532d, 0xcb2d, 0x4b2d, 0x2b2d, 0xab2d, 0xcd2d, 0x4d2d, 0x2d2d,
82 0xad2d, 0x352d, 0xb52d, 0xd52d, 0x552d, 0xccad, 0x4cad, 0x2cad, 0xacad,
83 0x34ad, 0xb4ad, 0xd4ad, 0x54ad, 0x32ad, 0xb2ad, 0xd2ad, 0x52ad, 0xcaad,
84 0x4aad, 0x2aad, 0xaaad, 0x3335, 0xb335, 0xd335, 0x5335, 0xcb35, 0x4b35,
85 0x2b35, 0xab35, 0xcd35, 0x4d35, 0x2d35, 0xad35, 0x3535, 0xb535, 0xd535,
86 0x5535, 0xccb5, 0x4cb5, 0x2cb5, 0xacb5, 0x34b5, 0xb4b5, 0xd4b5, 0x54b5,
87 0x32b5, 0xb2b5, 0xd2b5, 0x52b5, 0xcab5, 0x4ab5, 0x2ab5, 0xaab5, 0xccd5,
88 0x4cd5, 0x2cd5, 0xacd5, 0x34d5, 0xb4d5, 0xd4d5, 0x54d5, 0x32d5, 0xb2d5,
89 0xd2d5, 0x52d5, 0xcad5, 0x4ad5, 0x2ad5, 0xaad5, 0x3355, 0xb355, 0xd355,
90 0x5355, 0xcb55, 0x4b55, 0x2b55, 0xab55, 0xcd55, 0x4d55, 0x2d55, 0xad55,
91 0x3555, 0xb555, 0xd555, 0x5555,
92};
94
96static uint32_t *spdif_ptr = nullptr;
97
103struct SPDIFConfig : public AudioInfo {
105 bits_per_sample = 16;
106 channels = 2;
107 sample_rate = 44100;
108 }
109 int port_no = 0; // processor dependent port
111 int buffer_count = 30;
112 int buffer_size = 384;
113#if defined(RP2040_HOWER)
114 int pin_bck = -1;
115 uint32_t sys_clock = 192000;
116#endif
117};
118
127class SPDIFOutput : public AudioStream {
128 public:
130 SPDIFOutput() = default;
131
133 virtual ~SPDIFOutput() { end(); }
134
136 bool begin() { return begin(cfg); }
137
139 bool begin(SPDIFConfig config) {
140 TRACED();
141 cfg = config;
142 // Some validations to make sure that the config is valid
143 if (!(cfg.channels == 1 || cfg.channels == 2)) {
144 LOGE("Unsupported number of channels: %d", cfg.channels);
145 return false;
146 }
147 if (cfg.bits_per_sample != 16) {
148 LOGE("Unsupported bits per sample: %d - must be 16!",
150 return false;
151 }
152
153 if (i2sOn) {
154 i2s.end();
155 }
156
157 // initialize S/PDIF buffer
160
161 // Setup I2S
162 int sample_rate = cfg.sample_rate * BMC_BITS_FACTOR;
163 if (sample_rate==0){
164 TRACEE();
165 return false;
166 }
167 int bclk = sample_rate * I2S_BITS_PER_SAMPLE * I2S_CHANNELS;
168 int mclk = (I2S_BUG_MAGIC / bclk) * bclk; // use mclk for avoiding I2S bug
169
171 i2s_cfg.sample_rate = sample_rate;
172 i2s_cfg.channels = cfg.channels;
173#if defined(RP2040_HOWER)
174 if (cfg.sys_clock > 0) set_sys_clock_khz(cfg.sys_clock, false);
175 // RP2040 does not support -1 for no pin
176 if (cfg.pin_bck >= 0) {
177 i2s_cfg.pin_bck = cfg.pin_bck;
178 i2s_cfg.pin_ws = cfg.pin_bck + 1;
179 } else {
180 LOGE("pin_bck not defined");
181 }
182#elif defined(STM32)
183 // pins are fixed
184#else
185 // default logic for ESP32
186 i2s_cfg.pin_ws = -1;
187 i2s_cfg.pin_bck = -1;
188 i2s_cfg.pin_data = cfg.pin_data;
189#endif
190 i2s_cfg.buffer_count = cfg.buffer_count;
191 i2s_cfg.buffer_size = cfg.buffer_size;
192 i2s_cfg.bits_per_sample = I2S_BITS_PER_SAMPLE;
193 i2s_cfg.i2s_format = I2S_STD_FORMAT;
194 i2s_cfg.rx_tx_mode = TX_MODE;
195#ifdef ESP32
196 i2s_cfg.use_apll = true;
197# if USE_LEGACY_I2S
198 i2s_cfg.fixed_mclk = mclk;
199# endif
200#endif
202 return i2sOn;
203 }
204
205 void end() {
206 TRACED();
207 i2s.end();
208 i2sOn = false;
209 }
210
213 TRACED();
214 if (info.bits_per_sample != 16) {
215 LOGE("Unsupported bits per sample: %d - must be 16!",
217 }
221 || !i2sOn) {
225 begin(cfg);
226 }
227 }
228
232 return c;
233 }
234
236 size_t write(const uint8_t *data, size_t len) {
237 if (!i2sOn) return 0;
238 const uint8_t *p = data;
239 size_t result = 0;
240
241 while (p < (uint8_t *)data + len) {
242 // convert PCM 16bit data to BMC 32bit pulse pattern
243 if (cfg.channels == 2) {
244 *(spdif_ptr + 1) =
245 (uint32_t)(((bmc_tab[*p] << 16) ^ bmc_tab[*(p + 1)]) << 1) >> 1;
246 p += 2;
247 } else {
248 // must be one channels -> use the same value for both
249 *(spdif_ptr + 1) =
250 (uint32_t)(((bmc_tab[*p] << 16) ^ bmc_tab[*(p)]) << 1) >> 1;
251 p++;
252 }
253 result += 2;
254 spdif_ptr += 2; // advance to next audio data
255
257 // set block start preamble
259
260 i2s.write((uint8_t *)spdif_buf, sizeof(spdif_buf));
262 }
263 }
264
265 return result;
266 }
267
268 protected:
269 bool i2sOn = false;
272
273 // initialize S/PDIF buffer
274 void spdif_buf_init(void) {
275 TRACED();
276 size_t i;
278
279 for (i = 0; i < (size_t)SPDIF_BUF_ARRAY_SIZE; i += 2) {
281 }
282 }
283};
284
288
289} // namespace audio_tools
#define TRACED()
Definition AudioLoggerIDF.h:31
#define TRACEE()
Definition AudioLoggerIDF.h:34
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define BMC_MW_DIF
Definition SPDIFOutput.h:52
#define I2S_CHANNELS
Definition SPDIFOutput.h:33
#define BMC_BITS_FACTOR
Definition SPDIFOutput.h:35
#define I2S_BUG_MAGIC
Definition SPDIFOutput.h:42
#define SYNC_FLIP
Definition SPDIFOutput.h:54
#define SPDIF_BUF_ARRAY_SIZE
Definition SPDIFOutput.h:46
#define SPDIF_DATA_PIN
Definition SPDIFOutput.h:28
#define BMC_W
Definition SPDIFOutput.h:51
#define SYNC_OFFSET
Definition SPDIFOutput.h:53
#define I2S_BITS_PER_SAMPLE
Definition SPDIFOutput.h:32
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:123
AudioInfo info
Definition BaseStream.h:174
Configuration for ESP32 legacy i2s.
Definition I2SConfigESP32.h:24
We support the Stream interface for the I2S access. In addition we allow a separate mute pin which mi...
Definition I2SStream.h:33
bool begin()
Definition I2SStream.h:54
virtual size_t write(const uint8_t *data, size_t len)
Writes the audio data to I2S.
Definition I2SStream.h:118
void end()
Stops the I2S interface.
Definition I2SStream.h:85
Output as 16 bit stereo SPDIF on the I2S data output pin. For the time beeing only the ESP32 is offic...
Definition SPDIFOutput.h:127
SPDIFConfig defaultConfig()
Provides the default configuration.
Definition SPDIFOutput.h:230
I2SStream i2s
Definition SPDIFOutput.h:271
virtual ~SPDIFOutput()
destructor
Definition SPDIFOutput.h:133
SPDIFConfig cfg
Definition SPDIFOutput.h:270
void setAudioInfo(AudioInfo info)
Change the audio parameters.
Definition SPDIFOutput.h:212
bool begin()
Starting with last or default settings.
Definition SPDIFOutput.h:136
bool i2sOn
Definition SPDIFOutput.h:269
void spdif_buf_init(void)
Definition SPDIFOutput.h:274
SPDIFOutput()=default
default constructor
bool begin(SPDIFConfig config)
Start with the provided parameters.
Definition SPDIFOutput.h:139
void end()
Definition SPDIFOutput.h:205
size_t write(const uint8_t *data, size_t len)
Writes the audio data as SPDIF to the defined output pin.
Definition SPDIFOutput.h:236
@ TX_MODE
Definition AudioTypes.h:30
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
static uint32_t spdif_buf[(((192 *(64/8) *2)/2)/sizeof(uint32_t))]
Definition SPDIFOutput.h:95
static const int16_t * bmc_tab
Definition SPDIFOutput.h:93
@ I2S_STD_FORMAT
Definition AudioTypes.h:421
static const uint16_t bmc_tab_uint[256]
Definition SPDIFOutput.h:62
static uint32_t * spdif_ptr
Definition SPDIFOutput.h:96
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:512
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:55
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:57
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:59
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:61
SPDIF configuration.
Definition SPDIFOutput.h:103
int buffer_count
Definition SPDIFOutput.h:111
int pin_data
Definition SPDIFOutput.h:110
SPDIFConfig()
Definition SPDIFOutput.h:104
int port_no
Definition SPDIFOutput.h:109
int buffer_size
Definition SPDIFOutput.h:112