Merge pull request #307 from UrbanLienert/spdif-24bit

24bit SPDIF output
This commit is contained in:
philippe44
2023-09-22 15:26:29 -07:00
committed by GitHub

View File

@@ -93,6 +93,9 @@ static void (*pseudo_idle_chain)(uint32_t now);
#define CONFIG_AMP_GPIO_LEVEL 1
#endif
#define VUCP24 (0xCC)
#define VUCP24I (0x32)
extern struct outputstate output;
extern struct buffer *streambuf;
extern struct buffer *outputbuf;
@@ -115,6 +118,7 @@ static struct {
bool enabled;
u8_t *buf;
size_t count;
u8_t vucp;
} spdif;
static size_t dma_buf_frames;
static TaskHandle_t output_i2s_task;
@@ -130,7 +134,7 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
static void output_thread_i2s(void *arg);
static void i2s_stats(uint32_t now);
static void spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count);
static void spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count, u8_t *vucp);
static void (*jack_handler_chain)(bool inserted);
#define I2C_PORT 0
@@ -542,6 +546,7 @@ static void output_thread_i2s(void *arg) {
i2s_stop(CONFIG_I2S_NUM);
adac->power(ADAC_STANDBY);
spdif.count = 0;
spdif.vucp = VUCP24;
}
usleep(100000);
continue;
@@ -617,7 +622,7 @@ static void output_thread_i2s(void *arg) {
// need IRAM for speed but can't allocate a FRAME_BLOCK * 16, so process by smaller chunks
while (count < oframes) {
size_t chunk = min(SPDIF_BLOCK, oframes - count);
spdif_convert((ISAMPLE_T*) obuf + count * 2, chunk, (u32_t*) spdif.buf, &spdif.count);
spdif_convert((ISAMPLE_T*) obuf + count * 2, chunk, (u32_t*) spdif.buf, &spdif.count, &spdif.vucp);
i2s_write(CONFIG_I2S_NUM, spdif.buf, chunk * 16, &obytes, portMAX_DELAY);
bytes += obytes / (16 / BYTES_PER_FRAME);
count += chunk;
@@ -687,42 +692,39 @@ static void i2s_stats(uint32_t now) {
#define PREAMBLE_M (0xE2) //11100010
#define PREAMBLE_W (0xE4) //11100100
#define VUCP ((0xCC) << 24)
#define VUCP_MUTE ((0xD4) << 24) // To mute PCM, set VUCP = invalid.
static const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least significant bit first)
0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
0xcd4c, 0x4d4c, 0x2d4c, 0xad4c, 0x354c, 0xb54c, 0xd54c, 0x554c,
0x332c, 0xb32c, 0xd32c, 0x532c, 0xcb2c, 0x4b2c, 0x2b2c, 0xab2c,
0xcd2c, 0x4d2c, 0x2d2c, 0xad2c, 0x352c, 0xb52c, 0xd52c, 0x552c,
0xccac, 0x4cac, 0x2cac, 0xacac, 0x34ac, 0xb4ac, 0xd4ac, 0x54ac,
0x32ac, 0xb2ac, 0xd2ac, 0x52ac, 0xcaac, 0x4aac, 0x2aac, 0xaaac,
0x3334, 0xb334, 0xd334, 0x5334, 0xcb34, 0x4b34, 0x2b34, 0xab34,
0xcd34, 0x4d34, 0x2d34, 0xad34, 0x3534, 0xb534, 0xd534, 0x5534,
0xccb4, 0x4cb4, 0x2cb4, 0xacb4, 0x34b4, 0xb4b4, 0xd4b4, 0x54b4,
0x32b4, 0xb2b4, 0xd2b4, 0x52b4, 0xcab4, 0x4ab4, 0x2ab4, 0xaab4,
0xccd4, 0x4cd4, 0x2cd4, 0xacd4, 0x34d4, 0xb4d4, 0xd4d4, 0x54d4,
0x32d4, 0xb2d4, 0xd2d4, 0x52d4, 0xcad4, 0x4ad4, 0x2ad4, 0xaad4,
0x3354, 0xb354, 0xd354, 0x5354, 0xcb54, 0x4b54, 0x2b54, 0xab54,
0xcd54, 0x4d54, 0x2d54, 0xad54, 0x3554, 0xb554, 0xd554, 0x5554,
0x3332, 0xb332, 0xd332, 0x5332, 0xcb32, 0x4b32, 0x2b32, 0xab32,
0xcd32, 0x4d32, 0x2d32, 0xad32, 0x3532, 0xb532, 0xd532, 0x5532,
0xccb2, 0x4cb2, 0x2cb2, 0xacb2, 0x34b2, 0xb4b2, 0xd4b2, 0x54b2,
0x32b2, 0xb2b2, 0xd2b2, 0x52b2, 0xcab2, 0x4ab2, 0x2ab2, 0xaab2,
0xccd2, 0x4cd2, 0x2cd2, 0xacd2, 0x34d2, 0xb4d2, 0xd4d2, 0x54d2,
0x32d2, 0xb2d2, 0xd2d2, 0x52d2, 0xcad2, 0x4ad2, 0x2ad2, 0xaad2,
0x3352, 0xb352, 0xd352, 0x5352, 0xcb52, 0x4b52, 0x2b52, 0xab52,
0xcd52, 0x4d52, 0x2d52, 0xad52, 0x3552, 0xb552, 0xd552, 0x5552,
0xccca, 0x4cca, 0x2cca, 0xacca, 0x34ca, 0xb4ca, 0xd4ca, 0x54ca,
0x32ca, 0xb2ca, 0xd2ca, 0x52ca, 0xcaca, 0x4aca, 0x2aca, 0xaaca,
0x334a, 0xb34a, 0xd34a, 0x534a, 0xcb4a, 0x4b4a, 0x2b4a, 0xab4a,
0xcd4a, 0x4d4a, 0x2d4a, 0xad4a, 0x354a, 0xb54a, 0xd54a, 0x554a,
0x332a, 0xb32a, 0xd32a, 0x532a, 0xcb2a, 0x4b2a, 0x2b2a, 0xab2a,
0xcd2a, 0x4d2a, 0x2d2a, 0xad2a, 0x352a, 0xb52a, 0xd52a, 0x552a,
0xccaa, 0x4caa, 0x2caa, 0xacaa, 0x34aa, 0xb4aa, 0xd4aa, 0x54aa,
0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
static const u16_t spdif_bmclookup[256] = {
0xcccc, 0xb333, 0xd333, 0xaccc, 0xcb33, 0xb4cc, 0xd4cc, 0xab33,
0xcd33, 0xb2cc, 0xd2cc, 0xad33, 0xcacc, 0xb533, 0xd533, 0xaacc,
0xccb3, 0xb34c, 0xd34c, 0xacb3, 0xcb4c, 0xb4b3, 0xd4b3, 0xab4c,
0xcd4c, 0xb2b3, 0xd2b3, 0xad4c, 0xcab3, 0xb54c, 0xd54c, 0xaab3,
0xccd3, 0xb32c, 0xd32c, 0xacd3, 0xcb2c, 0xb4d3, 0xd4d3, 0xab2c,
0xcd2c, 0xb2d3, 0xd2d3, 0xad2c, 0xcad3, 0xb52c, 0xd52c, 0xaad3,
0xccac, 0xb353, 0xd353, 0xacac, 0xcb53, 0xb4ac, 0xd4ac, 0xab53,
0xcd53, 0xb2ac, 0xd2ac, 0xad53, 0xcaac, 0xb553, 0xd553, 0xaaac,
0xcccb, 0xb334, 0xd334, 0xaccb, 0xcb34, 0xb4cb, 0xd4cb, 0xab34,
0xcd34, 0xb2cb, 0xd2cb, 0xad34, 0xcacb, 0xb534, 0xd534, 0xaacb,
0xccb4, 0xb34b, 0xd34b, 0xacb4, 0xcb4b, 0xb4b4, 0xd4b4, 0xab4b,
0xcd4b, 0xb2b4, 0xd2b4, 0xad4b, 0xcab4, 0xb54b, 0xd54b, 0xaab4,
0xccd4, 0xb32b, 0xd32b, 0xacd4, 0xcb2b, 0xb4d4, 0xd4d4, 0xab2b,
0xcd2b, 0xb2d4, 0xd2d4, 0xad2b, 0xcad4, 0xb52b, 0xd52b, 0xaad4,
0xccab, 0xb354, 0xd354, 0xacab, 0xcb54, 0xb4ab, 0xd4ab, 0xab54,
0xcd54, 0xb2ab, 0xd2ab, 0xad54, 0xcaab, 0xb554, 0xd554, 0xaaab,
0xcccd, 0xb332, 0xd332, 0xaccd, 0xcb32, 0xb4cd, 0xd4cd, 0xab32,
0xcd32, 0xb2cd, 0xd2cd, 0xad32, 0xcacd, 0xb532, 0xd532, 0xaacd,
0xccb2, 0xb34d, 0xd34d, 0xacb2, 0xcb4d, 0xb4b2, 0xd4b2, 0xab4d,
0xcd4d, 0xb2b2, 0xd2b2, 0xad4d, 0xcab2, 0xb54d, 0xd54d, 0xaab2,
0xccd2, 0xb32d, 0xd32d, 0xacd2, 0xcb2d, 0xb4d2, 0xd4d2, 0xab2d,
0xcd2d, 0xb2d2, 0xd2d2, 0xad2d, 0xcad2, 0xb52d, 0xd52d, 0xaad2,
0xccad, 0xb352, 0xd352, 0xacad, 0xcb52, 0xb4ad, 0xd4ad, 0xab52,
0xcd52, 0xb2ad, 0xd2ad, 0xad52, 0xcaad, 0xb552, 0xd552, 0xaaad,
0xccca, 0xb335, 0xd335, 0xacca, 0xcb35, 0xb4ca, 0xd4ca, 0xab35,
0xcd35, 0xb2ca, 0xd2ca, 0xad35, 0xcaca, 0xb535, 0xd535, 0xaaca,
0xccb5, 0xb34a, 0xd34a, 0xacb5, 0xcb4a, 0xb4b5, 0xd4b5, 0xab4a,
0xcd4a, 0xb2b5, 0xd2b5, 0xad4a, 0xcab5, 0xb54a, 0xd54a, 0xaab5,
0xccd5, 0xb32a, 0xd32a, 0xacd5, 0xcb2a, 0xb4d5, 0xd4d5, 0xab2a,
0xcd2a, 0xb2d5, 0xd2d5, 0xad2a, 0xcad5, 0xb52a, 0xd52a, 0xaad5,
0xccaa, 0xb355, 0xd355, 0xacaa, 0xcb55, 0xb4aa, 0xd4aa, 0xab55,
0xcd55, 0xb2aa, 0xd2aa, 0xad55, 0xcaaa, 0xb555, 0xd555, 0xaaaa
};
/*
@@ -738,62 +740,70 @@ static const u16_t spdif_bmclookup[256] = { //biphase mark encoded values (least
The I2S interface must output first the B/M/W preamble which means that second
32 bits words must be first and so must be marked right channel.
*/
static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count) {
register u16_t hi, lo, aux;
static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst, size_t *count, u8_t *vucp) {
register u16_t hi, lo;
#if BYTES_PER_FRAME == 8
register u16_t aux;
#endif
register u8_t vu = *vucp;
size_t cnt = *count;
while (frames--) {
// start with left channel
#if BYTES_PER_FRAME == 4
hi = spdif_bmclookup[(u8_t)(*src >> 8)];
lo = spdif_bmclookup[(u8_t) *src++];
// invert if last preceeding bit is 1
lo ^= ~((s16_t)hi) >> 16;
// first 16 bits
aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17);
#else
hi = spdif_bmclookup[(u8_t)(*src >> 24)];
lo = spdif_bmclookup[(u8_t)(*src >> 16)];
// invert if last preceeding bit is 1
lo ^= ~((s16_t)hi) >> 16;
// first 16 bits
// we use 20 bits samples as we need to force parity
aux = spdif_bmclookup[(u8_t)(*src++ >> 12)];
aux = (u8_t) (aux ^ (~((s16_t)lo) >> 16));
aux |= (0xb3 ^ (((u16_t)((s8_t)aux)) >> 9)) << 8;
#endif
// set special preamble every 192 iteration
hi = spdif_bmclookup[(u8_t)(*src >> 8)];
lo = spdif_bmclookup[(u8_t)*src++];
if (lo & 1) hi = ~hi;
if (++cnt > 191) {
*dst++ = VUCP | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
*dst++ = (vu << 24) | (PREAMBLE_B << 16) | 0xCCCC;
cnt = 0;
} else {
*dst++ = VUCP | (PREAMBLE_M << 16) | aux;
}
// now write sample's 16 low bits
*dst++ = ((u32_t)lo << 16) | hi;
*dst++ = (vu << 24) | (PREAMBLE_M << 16) | 0xCCCC;
}
#else
hi = spdif_bmclookup[(u8_t)(*src >> 24)];
lo = spdif_bmclookup[(u8_t)(*src >> 16)];
aux = spdif_bmclookup[(u8_t)(*src++ >> 8)];
if (aux & 1) lo = ~lo;
if (lo & 1) hi = ~hi;
if (++cnt > 191) {
*dst++ = (vu << 24) | (PREAMBLE_B << 16) | aux;
cnt = 0;
} else {
*dst++ = (vu << 24) | (PREAMBLE_M << 16) | aux;
}
#endif
if (hi & 1) vu = VUCP24I;
else vu = VUCP24;
*dst++ = ((u32_t)lo << 16) | hi;
// then do right channel, no need to check PREAMBLE_B
#if BYTES_PER_FRAME == 4
hi = spdif_bmclookup[(u8_t)(*src >> 8)];
lo = spdif_bmclookup[(u8_t) *src++];
lo ^= ~((s16_t)hi) >> 16;
aux = 0xb333 ^ (((u32_t)((s16_t)lo)) >> 17);
hi = spdif_bmclookup[(u8_t)(*src >> 8)];
lo = spdif_bmclookup[(u8_t)*src++];
if (lo & 1) hi = ~hi;
*dst++ = (vu << 24) | (PREAMBLE_W << 16) | 0xCCCC;
#else
hi = spdif_bmclookup[(u8_t)(*src >> 24)];
lo = spdif_bmclookup[(u8_t)(*src >> 16)];
lo ^= ~((s16_t)hi) >> 16;
aux = spdif_bmclookup[(u8_t)(*src++ >> 12)];
aux = (u8_t) (aux ^ (~((s16_t)lo) >> 16));
aux |= (0xb3 ^ (((u16_t)((s8_t)aux)) >> 9)) << 8;
#endif
*dst++ = VUCP | (PREAMBLE_W << 16) | aux;
*dst++ = ((u32_t)lo << 16) | hi;
hi = spdif_bmclookup[(u8_t)(*src >> 24)];
lo = spdif_bmclookup[(u8_t)(*src >> 16)];
aux = spdif_bmclookup[(u8_t)(*src++ >> 8)];
if (aux & 1) lo = ~lo;
if (lo & 1) hi = ~hi;
*dst++ = (vu << 24) | (PREAMBLE_W << 16) | aux;
#endif
if (hi & 1) vu = VUCP24I;
else vu = VUCP24;
*dst++ = ((u32_t)lo << 16) | hi;
}
*count = cnt;
*vucp = vu;
}