SPDIF output: adding "copy permitted" in the channel status

jmarsh

Well-known member
Last week I tried to connect the Teensy's SPDIF output sinks to a cheap USB audio dongle (like this but from ebay and costing less than $10). It wouldn't pick up any sound, no matter if I used AudioOutputSPDIF, AudioOutputSPDIF2 or AudioOutputSPDIF3. Looking at the code I noticed the channel status was not implemented and suspected the lack of the "copy permitted" bit to be the problem. This was easy to check for AudioOutputSPDIF3 since the channel status bitstream is implemented as a register:
SPDIF_STCSCH = 1 << 21; // set copyright permitted bit in control channel

This was enough to get working audio from AudioOutputSPDIF3. But what about the I2S based AudioOutputSPDIF and AudioOutputSPDIF2?
They required a bit more work; for starters the VUCP_INVALID definition was incorrect and wasn't marking V as invalid at all, but was just setting U and C instead. I fixed that, added a mask to flip U and C (the total number of set bits must be even to keep the parity bit clear), reworked the encoding a bit and added a bit "array" based on the channel status:

This got audio working for AudioOutputSPDIF. But I wasn't going to duplicate all that work for AudioOutputSPDIF2; instead I templated the code so it could be used by both I2S-based SPDIF interfaces:

Now all three output interfaces work correctly with my USB dongle.
I know there's been a few queries in the past by people asking to implement the various AES-EBU standards over SPDIF, these are basically the same thing but with slightly different channel status formats. If someone was inclined they could extend my channel_status class to accommodate those, but for now I'm happy just having the copy_permitted bit set in the output.
 
Thanks for looking into this!
While viewing output_spdif.cpp, I noticed this struct:
C++:
    struct {
        // byte 0
        uint32_t pro:1; // 0=consumer format, 1=professional format
        uint32_t audio:1; // 0=linear PCM, 1=non-PCM
        uint32_t copy_permitted:1; // 0=copy inhibited, 1=copy permitted
        uint32_t pre_emphasis:3; // 0=none(2ch), 1=50/15us(2ch), 2/3=reserved(2ch), others=reserved(4ch)
        uint32_t mode:2; // 0=mode 0 (defines next 24 bits)
        // byte 1
        uint32_t category:7; // 0=general
        uint32_t generation:1; // 0=none/1st generation, 1=original/commercial
        // byte 2
        uint32_t source:4; // 0=unspecified
        uint32_t channel:4; // channel number, 0=unspecified
        // byte 3
        uint32_t Fs:4; // 0=44.1kHz, 2=48, 3=32, others=reserved
        uint32_t clock_accuracy:2; // 0=+/- 1000ppm, 1=+/- 50ppm, 2=variable, 3=reserved
        uint32_t :2; // reserved
    };
Just curious: where did you find this information?
Some time ago I searched for this information and I found the 2 attached documents. They seem to differ from your struct?

Paul
 

Attachments

  • SPDIF Channel Status Bits - Consumer config.pdf
    148.9 KB · Views: 75
  • SPDIF Channel Status Bits - Professional config.pdf
    148.8 KB · Views: 60
Hmm, confusing all those slightly different specifications.
I tend to trust the CirrusLogic/Crystal document since it specifies all 192 Channel Status bits for both Consumer and Professional mode.

Paul
 
Last week I tried to connect the Teensy's SPDIF output sinks to a cheap USB audio dongle (like this but from ebay and costing less than $10). It wouldn't pick up any sound, no matter if I used AudioOutputSPDIF, AudioOutputSPDIF2 or AudioOutputSPDIF3. Looking at the code I noticed the channel status was not implemented and suspected the lack of the "copy permitted" bit to be the problem. This was easy to check for AudioOutputSPDIF3 since the channel status bitstream is implemented as a register:
SPDIF_STCSCH = 1 << 21; // set copyright permitted bit in control channel
I can confirm that this works for AudioOutputSPDIF3.
Here is the output of my logic analyzer:
Code:
1514,2454717.50,Preamble B,
1515,2456135.00,Aux 0x0,Audio 0xf54800
1516,2457552.50,Sample 0x12af,
1517,2464640.00,V,
1518,2464992.50,S: 0,
1519,2465347.50,C: 0,     //this is Channel Status bit 0 of Channel A
1520,2465700.00,P: 0,
1521,2466057.50,Preamble W,
1522,2467475.00,Aux 0x0,Audio 0xf54800
1523,2468892.50,Sample 0x12af,
1524,2475977.50,V,
1525,2476332.50,S: 0,
1526,2476687.50,C: 0,     //this is Channel Status bit 0 of Channel B
1527,2477040.00,P: 0,
1528,2477395.00,Preamble M,
1529,2478812.50,Aux 0x0,Audio 0xf46600
1530,2480230.00,Sample 0x662f,
1531,2487317.50,V,
1532,2487672.50,S: 0,
1533,2488025.00,C: 0,     //this is Channel Status bit 1 of Channel A
1534,2488380.00,P: 1,
1535,2488735.00,Preamble W,
1536,2490152.50,Aux 0x0,Audio 0xf46600
1537,2491570.00,Sample 0x662f,
1538,2498657.50,V,
1539,2499012.50,S: 0,
1540,2499365.00,C: 0,     //this is Channel Status bit 1 of Channel B
1541,2499720.00,P: 1,
1542,2500075.00,Preamble M,
1543,2501492.50,Aux 0x0,Audio 0xf3bf00
1544,2502910.00,Sample 0xfdcf,
1545,2509995.00,V,
1546,2510350.00,S: 0,
1547,2510705.00,C: 1,      //this is Channel Status bit 2 of Channel A
1548,2511060.00,P: 0,
1549,2511415.00,Preamble W,
1550,2512832.50,Aux 0x0,Audio 0xf3bf00
1551,2514250.00,Sample 0xfdcf,
1552,2521335.00,V,
1553,2521690.00,S: 0,
1554,2522045.00,C: 1,      //this is Channel Status bit 2 of Channel B
1555,2522397.50,P: 0,
Before adding the line SPDIF_STCSCH = 1 << 21; // set copyright permitted bit in control channel, Channel Status bit 2 was set to "0" ["Copy inhibited"].

Paul
 
Just got hold of one of those interfaces, and tested out the changes. Have filed a PR for an issue with AudioOutputSPDIF2, but otherwise I can confirm all works as advertised :)

Thanks for this.
 
Yeah there was a typo in there that somehow didn't trigger a compilation fault... and then I looked at it and wondered why I changed that name in the first place. I think I did a partial revert to remove whitespace-only changes and it reset the file timestamp or something.
I can't imagine this being merged if I sent a PR to Paul due to "well-tested existing code works ok and this touches a lot of stuff" so not really fussed about it, it does what I need it to...
 
To be honest, I didn't look that closely at the scale of your changes, just merged them in (on a separate branch, of course...) and tested. They work ... previously that interface didn't ... I'm happy. "well-tested existing code works ok" is true up to a point (I hadn't had any issues with some other cheap S/PDIF interfaces), but since a counter-example has now been found I'd've thought a PR would merit some consideration, though probably not on this development cycle.

I for one definitely plan on having this in my default Audio library - the stock one just has way too many gremlins to consider living with.
 
Back
Top