Skip to content

Commit f8d9a35

Browse files
committed
SPDIF example1
1 parent dd8a4c5 commit f8d9a35

File tree

4 files changed

+240
-25
lines changed

4 files changed

+240
-25
lines changed
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
This example code is in the Public Domain (or CC0 licensed, at your option.)
3+
4+
Unless required by applicable law or agreed to in writing, this
5+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
6+
CONDITIONS OF ANY KIND, either express or implied.
7+
*/
8+
#include "freertos/FreeRTOS.h"
9+
#include "driver/i2s.h"
10+
11+
#ifdef CONFIG_SPDIF_DATA_PIN
12+
#define SPDIF_DATA_PIN CONFIG_SPDIF_DATA_PIN
13+
#else
14+
#define SPDIF_DATA_PIN 23
15+
#endif
16+
17+
#define I2S_NUM (0)
18+
19+
#define I2S_BITS_PER_SAMPLE (32)
20+
#define I2S_CHANNELS 2
21+
#define BMC_BITS_PER_SAMPLE 64
22+
#define BMC_BITS_FACTOR (BMC_BITS_PER_SAMPLE / I2S_BITS_PER_SAMPLE)
23+
#define SPDIF_BLOCK_SAMPLES 192
24+
#define SPDIF_BUF_DIV 2 // double buffering
25+
#define DMA_BUF_COUNT 2
26+
#define DMA_BUF_LEN (SPDIF_BLOCK_SAMPLES * BMC_BITS_PER_SAMPLE / I2S_BITS_PER_SAMPLE / SPDIF_BUF_DIV)
27+
#define I2S_BUG_MAGIC (26 * 1000 * 1000) // magic number for avoiding I2S bug
28+
#define SPDIF_BLOCK_SIZE (SPDIF_BLOCK_SAMPLES * (BMC_BITS_PER_SAMPLE/8) * I2S_CHANNELS)
29+
#define SPDIF_BUF_SIZE (SPDIF_BLOCK_SIZE / SPDIF_BUF_DIV)
30+
#define SPDIF_BUF_ARRAY_SIZE (SPDIF_BUF_SIZE / sizeof(uint32_t))
31+
32+
static uint32_t spdif_buf[SPDIF_BUF_ARRAY_SIZE];
33+
static uint32_t *spdif_ptr;
34+
35+
/*
36+
* 8bit PCM to 16bit BMC conversion table, LSb first, 1 end
37+
*/
38+
static const int16_t bmc_tab[256] = {
39+
0x3333, 0xb333, 0xd333, 0x5333, 0xcb33, 0x4b33, 0x2b33, 0xab33,
40+
0xcd33, 0x4d33, 0x2d33, 0xad33, 0x3533, 0xb533, 0xd533, 0x5533,
41+
0xccb3, 0x4cb3, 0x2cb3, 0xacb3, 0x34b3, 0xb4b3, 0xd4b3, 0x54b3,
42+
0x32b3, 0xb2b3, 0xd2b3, 0x52b3, 0xcab3, 0x4ab3, 0x2ab3, 0xaab3,
43+
0xccd3, 0x4cd3, 0x2cd3, 0xacd3, 0x34d3, 0xb4d3, 0xd4d3, 0x54d3,
44+
0x32d3, 0xb2d3, 0xd2d3, 0x52d3, 0xcad3, 0x4ad3, 0x2ad3, 0xaad3,
45+
0x3353, 0xb353, 0xd353, 0x5353, 0xcb53, 0x4b53, 0x2b53, 0xab53,
46+
0xcd53, 0x4d53, 0x2d53, 0xad53, 0x3553, 0xb553, 0xd553, 0x5553,
47+
0xcccb, 0x4ccb, 0x2ccb, 0xaccb, 0x34cb, 0xb4cb, 0xd4cb, 0x54cb,
48+
0x32cb, 0xb2cb, 0xd2cb, 0x52cb, 0xcacb, 0x4acb, 0x2acb, 0xaacb,
49+
0x334b, 0xb34b, 0xd34b, 0x534b, 0xcb4b, 0x4b4b, 0x2b4b, 0xab4b,
50+
0xcd4b, 0x4d4b, 0x2d4b, 0xad4b, 0x354b, 0xb54b, 0xd54b, 0x554b,
51+
0x332b, 0xb32b, 0xd32b, 0x532b, 0xcb2b, 0x4b2b, 0x2b2b, 0xab2b,
52+
0xcd2b, 0x4d2b, 0x2d2b, 0xad2b, 0x352b, 0xb52b, 0xd52b, 0x552b,
53+
0xccab, 0x4cab, 0x2cab, 0xacab, 0x34ab, 0xb4ab, 0xd4ab, 0x54ab,
54+
0x32ab, 0xb2ab, 0xd2ab, 0x52ab, 0xcaab, 0x4aab, 0x2aab, 0xaaab,
55+
0xcccd, 0x4ccd, 0x2ccd, 0xaccd, 0x34cd, 0xb4cd, 0xd4cd, 0x54cd,
56+
0x32cd, 0xb2cd, 0xd2cd, 0x52cd, 0xcacd, 0x4acd, 0x2acd, 0xaacd,
57+
0x334d, 0xb34d, 0xd34d, 0x534d, 0xcb4d, 0x4b4d, 0x2b4d, 0xab4d,
58+
0xcd4d, 0x4d4d, 0x2d4d, 0xad4d, 0x354d, 0xb54d, 0xd54d, 0x554d,
59+
0x332d, 0xb32d, 0xd32d, 0x532d, 0xcb2d, 0x4b2d, 0x2b2d, 0xab2d,
60+
0xcd2d, 0x4d2d, 0x2d2d, 0xad2d, 0x352d, 0xb52d, 0xd52d, 0x552d,
61+
0xccad, 0x4cad, 0x2cad, 0xacad, 0x34ad, 0xb4ad, 0xd4ad, 0x54ad,
62+
0x32ad, 0xb2ad, 0xd2ad, 0x52ad, 0xcaad, 0x4aad, 0x2aad, 0xaaad,
63+
0x3335, 0xb335, 0xd335, 0x5335, 0xcb35, 0x4b35, 0x2b35, 0xab35,
64+
0xcd35, 0x4d35, 0x2d35, 0xad35, 0x3535, 0xb535, 0xd535, 0x5535,
65+
0xccb5, 0x4cb5, 0x2cb5, 0xacb5, 0x34b5, 0xb4b5, 0xd4b5, 0x54b5,
66+
0x32b5, 0xb2b5, 0xd2b5, 0x52b5, 0xcab5, 0x4ab5, 0x2ab5, 0xaab5,
67+
0xccd5, 0x4cd5, 0x2cd5, 0xacd5, 0x34d5, 0xb4d5, 0xd4d5, 0x54d5,
68+
0x32d5, 0xb2d5, 0xd2d5, 0x52d5, 0xcad5, 0x4ad5, 0x2ad5, 0xaad5,
69+
0x3355, 0xb355, 0xd355, 0x5355, 0xcb55, 0x4b55, 0x2b55, 0xab55,
70+
0xcd55, 0x4d55, 0x2d55, 0xad55, 0x3555, 0xb555, 0xd555, 0x5555,
71+
};
72+
73+
// BMC preamble
74+
#define BMC_B 0x33173333 // block start
75+
#define BMC_M 0x331d3333 // left ch
76+
#define BMC_W 0x331b3333 // right ch
77+
#define BMC_MW_DIF (BMC_M ^ BMC_W)
78+
#define SYNC_OFFSET 2 // byte offset of SYNC
79+
#define SYNC_FLIP ((BMC_B ^ BMC_M) >> (SYNC_OFFSET * 8))
80+
81+
// initialize S/PDIF buffer
82+
static void spdif_buf_init(void)
83+
{
84+
int i;
85+
uint32_t bmc_mw = BMC_W;
86+
87+
for (i = 0; i < SPDIF_BUF_ARRAY_SIZE; i += 2) {
88+
spdif_buf[i] = bmc_mw ^= BMC_MW_DIF;
89+
}
90+
}
91+
92+
// initialize I2S for S/PDIF transmission
93+
void spdif_init(int rate)
94+
{
95+
int sample_rate = rate * BMC_BITS_FACTOR;
96+
int bclk = sample_rate * I2S_BITS_PER_SAMPLE * I2S_CHANNELS;
97+
int mclk = (I2S_BUG_MAGIC / bclk) * bclk; // use mclk for avoiding I2S bug
98+
i2s_config_t i2s_config = {
99+
.mode = I2S_MODE_MASTER | I2S_MODE_TX,
100+
.sample_rate = sample_rate,
101+
.bits_per_sample = I2S_BITS_PER_SAMPLE,
102+
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
103+
.communication_format = I2S_COMM_FORMAT_I2S,
104+
.intr_alloc_flags = 0,
105+
.dma_buf_count = DMA_BUF_COUNT,
106+
.dma_buf_len = DMA_BUF_LEN,
107+
.use_apll = true,
108+
.tx_desc_auto_clear = true,
109+
.fixed_mclk = mclk, // avoiding I2S bug
110+
};
111+
i2s_pin_config_t pin_config = {
112+
.bck_io_num = -1,
113+
.ws_io_num = -1,
114+
.data_out_num = SPDIF_DATA_PIN,
115+
.data_in_num = -1,
116+
};
117+
118+
ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL));
119+
ESP_ERROR_CHECK(i2s_set_pin(I2S_NUM, &pin_config));
120+
121+
// initialize S/PDIF buffer
122+
spdif_buf_init();
123+
spdif_ptr = spdif_buf;
124+
}
125+
126+
// write audio data to I2S buffer
127+
void spdif_write(const void *src, size_t size)
128+
{
129+
const uint8_t *p = src;
130+
131+
while (p < (uint8_t *)src + size) {
132+
133+
// convert PCM 16bit data to BMC 32bit pulse pattern
134+
*(spdif_ptr + 1) = (uint32_t)(((bmc_tab[*p] << 16) ^ bmc_tab[*(p + 1)]) << 1) >> 1;
135+
136+
p += 2;
137+
spdif_ptr += 2; // advance to next audio data
138+
139+
if (spdif_ptr >= &spdif_buf[SPDIF_BUF_ARRAY_SIZE]) {
140+
size_t i2s_write_len;
141+
142+
// set block start preamble
143+
((uint8_t *)spdif_buf)[SYNC_OFFSET] ^= SYNC_FLIP;
144+
145+
i2s_write(I2S_NUM, spdif_buf, sizeof(spdif_buf), &i2s_write_len, portMAX_DELAY);
146+
147+
spdif_ptr = spdif_buf;
148+
}
149+
}
150+
}
151+
152+
// change S/PDIF sample rate
153+
void spdif_set_sample_rates(int rate)
154+
{
155+
// uninstall and reinstall I2S driver for avoiding I2S bug
156+
i2s_driver_uninstall(I2S_NUM);
157+
spdif_init(rate);
158+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
This example code is in the Public Domain (or CC0 licensed, at your option.)
3+
4+
Unless required by applicable law or agreed to in writing, this
5+
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
6+
CONDITIONS OF ANY KIND, either express or implied.
7+
*/
8+
#include <stdint.h>
9+
#include <sys/types.h>
10+
11+
#ifdef __cplusplus
12+
extern "C" {
13+
#endif
14+
15+
/*
16+
* initialize S/PDIF driver
17+
* rate: sampling rate, 44100Hz, 48000Hz etc.
18+
*/
19+
void spdif_init(int rate);
20+
21+
/*
22+
* send PCM data to S/PDIF transmitter
23+
* src: pointer to 16bit PCM stereo data
24+
* size: number of data bytes
25+
*/
26+
void spdif_write(const void *src, size_t size);
27+
28+
/*
29+
* change sampling rate
30+
* rate: sampling rate, 44100Hz, 48000Hz etc.
31+
*/
32+
void spdif_set_sample_rates(int rate);
33+
34+
#ifdef __cplusplus
35+
}
36+
#endif
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* @file streams-generator-i2s.ino
3+
* @author Phil Schatzmann
4+
* @brief Test
5+
* @copyright GPLv3
6+
*/
7+
8+
#include "AudioTools.h"
9+
#include "spdif.h"
10+
11+
using namespace audio_tools;
12+
13+
typedef int16_t sound_t; // sound will be represented as int16_t (with 2 bytes)
14+
uint16_t sample_rate=44100;
15+
uint8_t channels = 2; // The stream will have 2 channels
16+
SineWaveGenerator<sound_t> sineWave(32000); // subclass of SoundGenerator with max amplitude of 32000
17+
GeneratedSoundStream<sound_t> sound(sineWave); // Stream generated from sine wave
18+
uint8_t buffer[1024];
19+
20+
// Arduino Setup
21+
void setup(void) {
22+
// Open Serial
23+
Serial.begin(115200);
24+
AudioLogger::instance().begin(Serial, AudioLogger::Info);
25+
26+
// start I2S
27+
Serial.println("starting SPDIF...");
28+
auto config = out.defaultConfig();
29+
config.sample_rate = sample_rate;
30+
config.channels = channels;
31+
config.pin_data = 23;
32+
out.begin(config);
33+
34+
// Setup sine wave
35+
sineWave.begin(channels, sample_rate, N_B4);
36+
Serial.println("started...");
37+
38+
spdif_init(sample_rate);
39+
}
40+
41+
// Arduino loop - copy sound to out
42+
void loop() {
43+
int read = sound.readBytes(buffer, 1024);
44+
spdif_write(buffer, read);
45+
46+
}

examples/sandbox/streams-generator-spdif_hex/out1.txt

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -225,28 +225,3 @@
225225
12:51:13.857 -> 33 33 1D 33 33 B3 32 55 - 33 33 1B 33 33 B3 32 55
226226
12:51:13.857 -> 33 33 1D 33 55 CD CA 34 - 33 33 1B 33 55 CD CA 34
227227
12:51:13.891 -> 33 33 1D 33 55 33 33 53 - 33 33 1B 33 55 33 33 53
228-
12:51:13.891 -> 33 33 1D 33 D5 54 4B 4B - 33 33 1B 33 D5 54 4B 4B
229-
12:51:13.891 -> 33 33 1D 33 B5 2A 35 2B - 33 33 1B 33 B5 2A 35 2B
230-
12:51:13.891 -> 33 33 1D 33 B5 D4 D4 2C - 33 33 1B 33 B5 D4 D4 2C
231-
12:51:13.891 -> 33 33 1D 33 35 D5 AC 2C - 33 33 1B 33 35 D5 AC 2C
232-
12:51:13.891 -> 33 33 1D 33 35 2B CB 2C - 33 33 1B 33 35 2B CB 2C
233-
12:51:13.891 -> 33 33 1D 33 AD 2A 4D 2D - 33 33 1B 33 AD 2A 4D 2D
234-
12:51:13.891 -> 33 33 1D 33 AD 54 CB 54 - 33 33 1B 33 AD 54 CB 54
235-
12:51:13.924 -> 33 33 1D 33 AD CC 34 55 - 33 33 1B 33 AD CC 34 55
236-
12:51:13.924 -> 33 33 1D 33 2D 4D AD 52 - 33 33 1B 33 2D 4D AD 52
237-
12:51:13.924 -> 33 33 1D 33 2D 53 4D 4B - 33 33 1B 33 2D 53 4D 4B
238-
12:51:13.924 -> 33 33 1D 33 4D B5 2A 4B - 33 33 1B 33 4D B5 2A 4B
239-
12:51:13.924 -> 33 33 1D 33 4D CD 52 33 - 33 33 1B 33 4D CD 52 33
240-
12:51:13.924 -> 33 33 1D 33 4D 53 55 55 - 33 33 1B 33 4D 53 55 55
241-
12:51:13.924 -> 33 33 1D 33 CD AA 4A 2B - 33 33 1B 33 CD AA 4A 2B
242-
12:51:13.957 -> 33 33 1D 33 CD CA AC 4A - 33 33 1B 33 CD CA AC 4A
243-
12:51:13.957 -> 33 33 1D 33 CD B2 AC 34 - 33 33 1B 33 CD B2 AC 34
244-
12:51:13.957 -> 33 33 1D 33 CD D4 CA 34 - 33 33 1B 33 CD D4 CA 34
245-
12:51:13.957 -> 33 33 1D 33 CD 34 D5 4A - 33 33 1B 33 CD 34 D5 4A
246-
12:51:13.957 -> 33 33 1D 33 CD AC 52 53 - 33 33 1B 33 CD AC 52 53
247-
12:51:13.957 -> 33 33 1D 33 CD AC 4C 4B - 33 33 1B 33 CD AC 4C 4B
248-
12:51:13.957 -> 33 33 1D 33 CD AC 4C 4B - 33 33 1B 33 CD AC 4C 4B
249-
12:51:13.991 -> 33 33 1D 33 CD AC 52 4B - 33 33 1B 33 CD AC 52 4B
250-
12:51:13.991 -> 33 33 1D 33 CD 34 55 33 - 33 33 1B 33 CD 34 55 33
251-
12:51:13.991 -> 33 33 1D 33 CD D4 CA 32 - 33 33 1B 33 CD D4 CA 32
252-
12:51:13.991 -> 33 33 1D 33 CD B2 AC 4A - 33 33 1B 33 CD B2 AC 4A

0 commit comments

Comments
 (0)