arduino-audio-tools
SPDIFOutput.h
1 
18 #pragma once
19 
20 #include "AudioConfig.h"
21 #include "AudioTools/CoreAudio/AudioLogger.h"
22 #include "AudioTools/CoreAudio/AudioStreams.h"
23 #include "AudioTools/CoreAudio/AudioI2S/I2SConfig.h"
24 #include "AudioTools/CoreAudio/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 #ifndef STM32
169  i2s_cfg.pin_ws = -1;
170  i2s_cfg.pin_bck = -1;
171  i2s_cfg.pin_data = cfg.pin_data;
172 #endif
173  i2s_cfg.buffer_count = cfg.buffer_count;
174  i2s_cfg.buffer_size = cfg.buffer_size;
175  i2s_cfg.bits_per_sample = I2S_BITS_PER_SAMPLE;
176  i2s_cfg.i2s_format = I2S_STD_FORMAT;
177  i2s_cfg.rx_tx_mode = TX_MODE;
178 #ifdef ESP32
179  i2s_cfg.use_apll = true;
180 # if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0 , 0)
181  i2s_cfg.fixed_mclk = mclk;
182 # endif
183 #endif
184  i2sOn = i2s.begin(i2s_cfg);
185  return i2sOn;
186  }
187 
188  void end() {
189  TRACED();
190  i2s.end();
191  i2sOn = false;
192  }
193 
195  void setAudioInfo(AudioInfo info) {
196  TRACED();
197  if (info.bits_per_sample != 16) {
198  LOGE("Unsupported bits per sample: %d - must be 16!",
199  info.bits_per_sample);
200  }
201  if (cfg.bits_per_sample != info.bits_per_sample
202  || cfg.channels != info.channels
203  || cfg.sample_rate != info.sample_rate
204  || !i2sOn) {
205  cfg.bits_per_sample = info.bits_per_sample;
206  cfg.channels = info.channels;
207  cfg.sample_rate = info.sample_rate;
208  begin(cfg);
209  }
210  }
211 
214  SPDIFConfig c;
215  return c;
216  }
217 
219  size_t write(const uint8_t *data, size_t len) {
220  if (!i2sOn) return 0;
221  const uint8_t *p = data;
222  size_t result = 0;
223 
224  while (p < (uint8_t *)data + len) {
225  // convert PCM 16bit data to BMC 32bit pulse pattern
226  if (cfg.channels == 2) {
227  *(spdif_ptr + 1) =
228  (uint32_t)(((bmc_tab[*p] << 16) ^ bmc_tab[*(p + 1)]) << 1) >> 1;
229  p += 2;
230  } else {
231  // must be one channels -> use the same value for both
232  *(spdif_ptr + 1) =
233  (uint32_t)(((bmc_tab[*p] << 16) ^ bmc_tab[*(p)]) << 1) >> 1;
234  p++;
235  }
236  result += 2;
237  spdif_ptr += 2; // advance to next audio data
238 
239  if (spdif_ptr >= &spdif_buf[SPDIF_BUF_ARRAY_SIZE]) {
240  // set block start preamble
241  ((uint8_t *)spdif_buf)[SYNC_OFFSET] ^= SYNC_FLIP;
242 
243  i2s.write((uint8_t *)spdif_buf, sizeof(spdif_buf));
244  spdif_ptr = spdif_buf;
245  }
246  }
247 
248  return result;
249  }
250 
251  protected:
252  bool i2sOn = false;
253  SPDIFConfig cfg;
254  I2SStream i2s;
255 
256  // initialize S/PDIF buffer
257  void spdif_buf_init(void) {
258  TRACED();
259  size_t i;
260  uint32_t bmc_mw = BMC_W;
261 
262  for (i = 0; i < (size_t)SPDIF_BUF_ARRAY_SIZE; i += 2) {
263  spdif_buf[i] = bmc_mw ^= BMC_MW_DIF;
264  }
265  }
266 };
267 
268 using SPDIFStream = SPDIFOutput;
269 
270 } // namespace audio_tools
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: BaseStream.h:109
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:33
virtual size_t write(const uint8_t *data, size_t len)
Writes the audio data to I2S.
Definition: I2SStream.h:115
void end()
Stops the I2S interface.
Definition: I2SStream.h:84
Output as 16 bit stereo SPDIF on the I2S data output pin.
Definition: SPDIFOutput.h:122
SPDIFConfig defaultConfig()
Provides the default configuration.
Definition: SPDIFOutput.h:213
virtual ~SPDIFOutput()
destructor
Definition: SPDIFOutput.h:128
void setAudioInfo(AudioInfo info)
Change the audio parameters.
Definition: SPDIFOutput.h:195
bool begin()
Starting with last or default settings.
Definition: SPDIFOutput.h:131
SPDIFOutput()=default
default constructor
bool begin(SPDIFConfig config)
Start with the provided parameters.
Definition: SPDIFOutput.h:134
size_t write(const uint8_t *data, size_t len)
Writes the audio data as SPDIF to the defined output pin.
Definition: SPDIFOutput.h:219
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:821
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: SPDIFOutput.h:103