arduino-audio-tools
AudioSPDIF.h
Go to the documentation of this file.
1 
18 #pragma once
19 
20 #include "AudioConfig.h"
21 #include "AudioTools/AudioLogger.h"
22 #include "AudioTools/AudioStreams.h"
23 #include "AudioI2S/I2SConfig.h"
24 #include "AudioI2S/I2SStream.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 
57 namespace audio_tools {
58 
59 /*
60  * 8bit PCM to 16bit BMC conversion table, LSb first, 1 end
61  */
62 static 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 };
93 static const int16_t *bmc_tab = (int16_t *)bmc_tab_uint;
94 
95 static uint32_t spdif_buf[SPDIF_BUF_ARRAY_SIZE];
96 static uint32_t *spdif_ptr = nullptr;
97 
103 struct SPDIFConfig : public AudioInfo {
104  SPDIFConfig() {
105  bits_per_sample = 16;
106  channels = 2;
107  sample_rate = 44100;
108  }
109  int port_no = 0; // processor dependent port
110  int pin_data = SPDIF_DATA_PIN;
111  int buffer_count = 30;
112  int buffer_size = 384;
113 };
114 
122 class SPDIFOutput : public AudioStream {
123  public:
125  SPDIFOutput() = default;
126 
128  virtual ~SPDIFOutput() { end(); }
129 
131  bool begin() { return begin(cfg); }
132 
134  bool begin(SPDIFConfig config) {
135  TRACED();
136  cfg = config;
137  // Some validations to make sure that the config is valid
138  if (!(cfg.channels == 1 || cfg.channels == 2)) {
139  LOGE("Unsupported number of channels: %d", cfg.channels);
140  return false;
141  }
142  if (cfg.bits_per_sample != 16) {
143  LOGE("Unsupported bits per sample: %d - must be 16!",
144  cfg.bits_per_sample);
145  return false;
146  }
147 
148  if (i2sOn) {
149  i2s.end();
150  }
151 
152  // initialize S/PDIF buffer
153  spdif_buf_init();
154  spdif_ptr = spdif_buf;
155 
156  // Setup I2S
157  int sample_rate = cfg.sample_rate * BMC_BITS_FACTOR;
158  if (sample_rate==0){
159  TRACEE();
160  return false;
161  }
162  int bclk = sample_rate * I2S_BITS_PER_SAMPLE * I2S_CHANNELS;
163  int mclk = (I2S_BUG_MAGIC / bclk) * bclk; // use mclk for avoiding I2S bug
164 
165  I2SConfig i2s_cfg;
166  i2s_cfg.sample_rate = sample_rate;
167  i2s_cfg.channels = cfg.channels;
168  i2s_cfg.pin_ws = -1;
169  i2s_cfg.pin_bck = -1;
170  i2s_cfg.pin_data = cfg.pin_data;
171  i2s_cfg.buffer_count = cfg.buffer_count;
172  i2s_cfg.buffer_size = cfg.buffer_size;
173  i2s_cfg.bits_per_sample = I2S_BITS_PER_SAMPLE;
174  i2s_cfg.i2s_format = I2S_STD_FORMAT;
175  i2s_cfg.rx_tx_mode = TX_MODE;
176 #ifdef ESP32
177  i2s_cfg.use_apll = true;
178 # if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0 , 0)
179  i2s_cfg.fixed_mclk = mclk;
180 # endif
181 #endif
182  i2sOn = i2s.begin(i2s_cfg);
183  return i2sOn;
184  }
185 
186  void end() {
187  TRACED();
188  i2s.end();
189  i2sOn = false;
190  }
191 
193  void setAudioInfo(AudioInfo info) {
194  TRACED();
195  if (info.bits_per_sample != 16) {
196  LOGE("Unsupported bits per sample: %d - must be 16!",
197  info.bits_per_sample);
198  }
199  if (cfg.bits_per_sample != info.bits_per_sample
200  || cfg.channels != info.channels
201  || cfg.sample_rate != info.sample_rate
202  || !i2sOn) {
203  cfg.bits_per_sample = info.bits_per_sample;
204  cfg.channels = info.channels;
205  cfg.sample_rate = info.sample_rate;
206  begin(cfg);
207  }
208  }
209 
212  SPDIFConfig c;
213  return c;
214  }
215 
217  size_t write(const uint8_t *src, size_t size) {
218  if (!i2sOn) return 0;
219  const uint8_t *p = src;
220  size_t result = 0;
221 
222  while (p < (uint8_t *)src + size) {
223  // convert PCM 16bit data to BMC 32bit pulse pattern
224  if (cfg.channels == 2) {
225  *(spdif_ptr + 1) =
226  (uint32_t)(((bmc_tab[*p] << 16) ^ bmc_tab[*(p + 1)]) << 1) >> 1;
227  p += 2;
228  } else {
229  // must be one channels -> use the same value for both
230  *(spdif_ptr + 1) =
231  (uint32_t)(((bmc_tab[*p] << 16) ^ bmc_tab[*(p)]) << 1) >> 1;
232  p++;
233  }
234  result += 2;
235  spdif_ptr += 2; // advance to next audio data
236 
237  if (spdif_ptr >= &spdif_buf[SPDIF_BUF_ARRAY_SIZE]) {
238  // set block start preamble
239  ((uint8_t *)spdif_buf)[SYNC_OFFSET] ^= SYNC_FLIP;
240 
241  i2s.write((uint8_t *)spdif_buf, sizeof(spdif_buf));
242  spdif_ptr = spdif_buf;
243  }
244  }
245 
246  return result;
247  }
248 
249  protected:
250  bool i2sOn = false;
251  SPDIFConfig cfg;
252  I2SStream i2s;
253 
254  // initialize S/PDIF buffer
255  void spdif_buf_init(void) {
256  TRACED();
257  size_t i;
258  uint32_t bmc_mw = BMC_W;
259 
260  for (i = 0; i < (size_t)SPDIF_BUF_ARRAY_SIZE; i += 2) {
261  spdif_buf[i] = bmc_mw ^= BMC_MW_DIF;
262  }
263  }
264 };
265 
266 using SPDIFStream = SPDIFOutput;
267 
268 } // namespace audio_tools
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: AudioStreams.h:24
Configuration for ESP32 legacy i2s.
Definition: I2SConfigESP32.h:23
RxTxMode rx_tx_mode
public settings
Definition: I2SConfigESP32.h:59
We support the Stream interface for the I2S access. In addition we allow a separate mute pin which mi...
Definition: I2SStream.h:31
virtual size_t write(const uint8_t *buffer, size_t size)
Writes the audio data to I2S.
Definition: I2SStream.h:105
void end()
Stops the I2S interface.
Definition: I2SStream.h:74
Output as 16 bit stereo SPDIF on the I2S data output pin.
Definition: AudioSPDIF.h:122
SPDIFConfig defaultConfig()
Provides the default configuration.
Definition: AudioSPDIF.h:211
virtual ~SPDIFOutput()
destructor
Definition: AudioSPDIF.h:128
void setAudioInfo(AudioInfo info)
Change the audio parameters.
Definition: AudioSPDIF.h:193
bool begin()
Starting with last or default settings.
Definition: AudioSPDIF.h:131
SPDIFOutput()=default
default constructor
bool begin(SPDIFConfig config)
Start with the provided parameters.
Definition: AudioSPDIF.h:134
size_t write(const uint8_t *src, size_t size)
Writes the audio data as SPDIF to the defined output pin.
Definition: AudioSPDIF.h:217
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:50
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
SPDIF configuration.
Definition: AudioSPDIF.h:103