ADAT white noise on Teensy 4.0

Ah, I've mixed up something, you've ben talking about the BNC signal, this is exactly the sample rate. So for 44.1kHz, that is a 44.1kHz signal.

I've measured the E-Mu, wich has 1 V p-p into 75 Ohm, capacitively coupled, unbalanced. High voltage for left sample.

There is also a studio clock with time code, which is the mentioned AES signal.

So, to make use of the BNC signal, we'd need some sort of PLL to upvert it to the mclk?

LRCLK is actually the same as the BNC wordclock, as much as I can see. So the bit clocks need to be derived from that to be in sync and in phase. right?
 
Last edited:
Thats the one, BNC word clock seemed like a good idea, It is a common way to sync clocks among interfaces in a studio situation.

Unless I have got behind with the times, most studio audio interfaces don't have an AES out, just ADAT and BNC word clock. afaik AES is common on live setups such as digital mixing consoles to talk to stage boxes and the like.

Do you know of a suitable PLL to convert the BNC word clock to mclk? We would also need some way of knowing in the software that the signal is being received and the teensy is locked to it.
 
In our studio, it is AES with SMPTE timecode, that also syncs the mixer, the video tape (yes..) and all the other ADAT and MIDI stuff. There is, as said, the BNC signal as well, which is not used.

Confirmed now with a dual channel scope. LRCLK is actually the same as the BNC wordclock. The left sample is on, while the BNC wordclock is high.
So the bit clocks need to be derived from that to be in sync and in phase. right?
 
Last edited:
Fair enough! that doesn't sound like a setup I am familiar with at all.
From what Frank was saying it sounds like it would be fairly easy to sync a Teensy to that because you can just use spdif in (basically AES as you know) to drive the I2S slave. I don't really want to use up the optical out on my RME but if you make progress let me know it would be cool to use a nicer clock.
 
AES can certainly be used to sync to but it’s not as accurate as a BNC word clock which is still the go to standard. It might be more convenient to just go with AES and not run BNCs since it’s twice the number of cables, but BNC will always be the best since it runs directly to a DAC or ADC.
 
Optical fibers for ADAT can be spliced into a Y, to get two same outputs with half light intensity from one, provided it is accurately made. There is usually enough power for this.

The AES signal is phase coupled and sample exact as well, so there is no such thing like "better" or "not so accurate".

The BNC signal is just simpler by means of signal complexity, but challenges how to get a bclk or mclk from it. Also, the BNC signal is picky about cables, because if the edges get "washed out" by bad cabling or lacking termination, it turns useless or even worse than having no signal at all.

So for our purpose at first glance it seems to be easier to use the AES or S/PDIF whatever signal, because is is decoded by S/PDIF input and provides an inherent word clock as well as a bit clock.

But let's see if we can use the BNC word clock. It seems to be more common in the audio-only world, so this is well worth the effort!
 
I am happy either way! If I can slave my teensy to my RME at 48k then I will be on top of the world
 
Looks like it does not use mclk:

Code:
    // not using MCLK in slave mode - hope that's ok?
    //CORE_PIN23_CONFIG = 3;  // AD_B1_09  ALT3=SAI1_MCLK
    CORE_PIN21_CONFIG = 3;  // AD_B1_11  ALT3=SAI1_RX_BCLK
    CORE_PIN20_CONFIG = 3;  // AD_B1_10  ALT3=SAI1_RX_SYNC 
    IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868
    IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872
So, all what's missing is sync (is above an input as well) - i think that can be generated internally, as in master mode.
All what we need then is a 48khz clock signal...
 
It's not the bit clock that you need for BNC word clock, you need frame sync which is set in the I2S1_TCR4 register bit zero, from the data sheet:
Code:
Frame Sync Direction
Configures the direction of the frame sync.
0b - Frame sync is generated externally in Slave mode. 
1b - Frame sync is generated internally in Master mode.

Then you just have to configure the correct pin and feed it the word clock signal.
 
I guess I can confirm that the ADAT OUT on T4.1 works like a charm ):

Soon I will release a complete -- output_adat.h and output_adat.cpp for T3.x and T4.x. as soon as I can find Teensy 3.x for testing.
 
;)
I've got Teensy 3.0 BLACK ): but the ATAT_DrumSamplePlayer sketch will not fit in region `FLASH'
1.8.19/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld.exe: region `FLASH' overflowed by 77720 bytes.
T_30.jpg
I guess I need to find something newer :cool:
 
I guess I need to find something newer :cool:

Or use waveform synth rather than samples stored in flash. Or just use fewer / smaller samples.

But yeah, 128K flash on the original Teensy 3.0 is kinda limiting when you want to include any sound samples or bitmap images compiled into the program.
 
I tried to use less samples that was easy.
\libraries\Audio\examples\HardwareTesting\ADAT_DrumSamplePlayer
Code:
void loop() {

  sine1.amplitude(1.0);

  sound0.play(AudioSampleKick);


  delay(250);

  sound1.play(AudioSampleHihat);

  delay(250);

  sound1.play(AudioSampleHihat);

  sound2.play(AudioSampleSnare);

  delay(250);

  sound1.play(AudioSampleHihat);

  delay(250);

  sound0.play(AudioSampleKick);

  sound3.play(AudioSampleHihat);

[COLOR="#FF0000"]  // sound3.play(AudioSampleGong);[/COLOR]

  delay(1000);

}
Got pass the memory issue but still no go on Teensy 3.0, I managed to salvage Teensy 3.1 and woohoo it works.

Perhaps it's the dma channels ?
Tech Specifications T3_0 T3_2.jpg
 
I'm having a bit bigger issues here on Teensy 4.x master clock frequency.

This has something to do with the calculations in this part:
Code:
     // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (freq * 256 * n1);
  double C = ((double)freq * 256 * n1 * n2) / 24000000;
  int c0 = C;
  int c2 = 10000;
  int c1 = C * c2 - (c0 * c2);

if I use double samplerate 88200 to get MCLK @22,579,200MHz I am actually getting 22,579,125MHz
Code:
   //debug only:
  Serial.printf("SetI2SFreq(%d)\n",freq);
  //Serial.print("Freq "),Serial.println(freq);
  Serial.print("c0 DIV   "), Serial.println(c0);
  Serial.print("c1 NUM   "), Serial.println(c1);
  Serial.print("c2 DENOM "), Serial.println(c2);
  Serial.print("n1 PRED/ "), Serial.println(n1);
  Serial.print("n2 PODF/ "), Serial.println(n2);

// -- Freq 88200    for 44100kHz ADAT, MCLK1 22,579,125Mhz -- we need 22,579,200MHz
// c0 DIV   30      -- OSC_24000000Mhz *(30 + (1050/10000)) = 722,532,000MHz
// c1 NUM   1055    <--- issue
// c2 DENOM 10000
// n1 PRED/ 4       -- 722,532,000 / 4 = 180,633,000MHz
// n2 PODF/ 8       -- 22,579,125
// MCLK--22,579,125 MHz

// -- Freq 88200   for 44100kHz ADAT, MCLK1 22,579,200Mhz
// c0 DIV   30
// c1 NUM   1056     <--- good bugfix @88200
// c2 DENOM 10000
// n1 PRED/ 4
// n2 PODF/ 8
// MCLK--22,579,200 MHz -- good @88200
PLL4 CLOCK.jpg
 
Last edited:
Ok for other frequencies it's all good only the 88200 I'm having this problem.
Code:
// -- Freq 96000   for 48000kHz ADAT, MCLK1 24,576,000Mhz
// c0 DIV   28
// c1 NUM   6720
// c2 DENOM 10000
// n1 PRED/ 4
// n2 PODF/ 7  
// MCLK-- 24,576,000 MHz   good@48000

// -- Freq 48000    for 48000kHz, MCLK1 12,288,000Mhz
// c0 DIV   28      -- OSC_24000000Mhz *(28 + (6720/10000)) = 688,128,000 MHz
// c1 NUM   6720    
// c2 DENOM 10000
// n1 PRED/ 4       -- 688,128,000 / 4 = 172,032,000 MHz
// n2 PODF/ 14      -- 12,288,000
// MCLK--12,288,000 MHz -- good @48000

// -- Freq 44100
// c0 DIV   28
// c1 NUM   2240
// c2 DENOM 10000
// n1 PRED/ 4
// n2 PODF/ 15
// MCLK--11,289,600 MHz -- good @44100
 
Ok here I am releasing complete output_adat.h and output_adat.cpp files for T.3.x and T4.x.
I've been testing these on T3.1 and T4.1.

Chris O. 3/25/2023
Fix compile error on Teensy LC
T4.x -PLL frequency correction @88200 for ADAT @44100, MCLK-22,579,200MHz was 22,579,125MHz
T4.x -Disabled receiver, ADAT IN not implemented yet.
T4.x -Set Transmit FIFO Watermark 1, was 0.

-- GUItool: AudioOutputADAT --> adat1 --

T3.x - T4.x Implemented Mute/Unmute on all 8ch. ADAT
adat1.mute_PCM(bool mute); // 1 mute - 0 unmute

Implemented Teensy 3.x - 4.x simple way to change the samplerate of ADAT at runtime.
NOTE: 44100(default) or 48000 ADAT Only 8ch, No ADAT SMUX II-IV yet.
!!! But be aware that not the entire lib will work. Not all parts of it support that!
adat1.set_Freq(44100);
adat1.set_Freq(48000);
This needs edit in output_adat.h public: class -- or use the provided output_adat.h here.
void set_Freq(int freq); // change the samplerate of ADAT at runtime, 44100 or 48000 only!!!

output_adat.cpp
Code:
/* ADAT for Teensy 4.X
   Copyright (c) 2017, Ernstjan Freriks,
   Thanks to Frank Bösing & KPC & Paul Stoffregen!

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice, development funding notice, and this permission
   notice shall be included in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

// Chris O. 3/25/2023
// Fix compile error on Teensy LC
// T4.x -PLL frequency correction @88200 for ADAT @44100, MCLK-22,579,200MHz was 22,579,125MHz
// T4.x -Disabled receiver, ADAT IN not implemented yet.
// T4.x -Set Transmit FIFO Watermark 1, was 0.
//
// -- GUItool: AudioOutputADAT  -->  adat1 --
// T3.x - T4.x Implemented Mute/Unmute on all 8ch. ADAT
//  adat1.mute_PCM(bool mute); // 1 mute - 0 unmute
//
// Implemented Teensy 3.x - 4.x simple way to change the samplerate of ADAT at runtime.
// NOTE: 44100(default) or 48000 ADAT Only 8ch, No ADAT SMUX II-IV yet.
// !!! But be aware that not the entire lib will work. Not all parts of it support that!
//  adat1.set_Freq(44100);
//  adat1.set_Freq(48000);
// This needs edit in output_adat.h   public: class
// void set_Freq(int freq); // change the samplerate of ADAT at runtime, 44100 or 48000 only!!!

#include <Arduino.h>
#include "output_adat.h"
#include "utility/imxrt_hw.h"

#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__)

audio_block_t * AudioOutputADAT::block_ch1_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch2_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch3_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch4_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch5_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch6_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch7_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch8_1st = NULL;

audio_block_t * AudioOutputADAT::block_ch1_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch2_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch3_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch4_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch5_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch6_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch7_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch8_2nd = NULL;

uint16_t  AudioOutputADAT::ch1_offset = 0;
uint16_t  AudioOutputADAT::ch2_offset = 0;
uint16_t  AudioOutputADAT::ch3_offset = 0;
uint16_t  AudioOutputADAT::ch4_offset = 0;
uint16_t  AudioOutputADAT::ch5_offset = 0;
uint16_t  AudioOutputADAT::ch6_offset = 0;
uint16_t  AudioOutputADAT::ch7_offset = 0;
uint16_t  AudioOutputADAT::ch8_offset = 0;

bool AudioOutputADAT::update_responsibility = false;

DMAMEM __attribute__((aligned(32))) static uint32_t ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8]; //4 KB, AUDIO_BLOCK_SAMPLES is usually 128
DMAChannel AudioOutputADAT::dma(false);

static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES / 4] = {0};

// These are the lookup tables. There are four of them so that the remainder of the 32 bit result can easily be XORred with the next 8 bits.
static const uint32_t LookupTable_firstword[256]  = { 0x0, 0x1ffffff, 0x3ffffff, 0x2000000, 0x7ffffff, 0x6000000, 0x4000000, 0x5ffffff, 0xfffffff, 0xe000000, 0xc000000, 0xdffffff, 0x8000000, 0x9ffffff, 0xbffffff, 0xa000000, 0x1fffffff, 0x1e000000, 0x1c000000, 0x1dffffff, 0x18000000, 0x19ffffff, 0x1bffffff, 0x1a000000, 0x10000000, 0x11ffffff, 0x13ffffff, 0x12000000, 0x17ffffff, 0x16000000, 0x14000000, 0x15ffffff, 0x3fffffff, 0x3e000000, 0x3c000000, 0x3dffffff, 0x38000000, 0x39ffffff, 0x3bffffff, 0x3a000000, 0x30000000, 0x31ffffff, 0x33ffffff, 0x32000000, 0x37ffffff, 0x36000000, 0x34000000, 0x35ffffff, 0x20000000, 0x21ffffff, 0x23ffffff, 0x22000000, 0x27ffffff, 0x26000000, 0x24000000, 0x25ffffff, 0x2fffffff, 0x2e000000, 0x2c000000, 0x2dffffff, 0x28000000, 0x29ffffff, 0x2bffffff, 0x2a000000, 0x7fffffff, 0x7e000000, 0x7c000000, 0x7dffffff, 0x78000000, 0x79ffffff, 0x7bffffff, 0x7a000000, 0x70000000, 0x71ffffff, 0x73ffffff, 0x72000000, 0x77ffffff, 0x76000000, 0x74000000, 0x75ffffff, 0x60000000, 0x61ffffff, 0x63ffffff, 0x62000000, 0x67ffffff, 0x66000000, 0x64000000, 0x65ffffff, 0x6fffffff, 0x6e000000, 0x6c000000, 0x6dffffff, 0x68000000, 0x69ffffff, 0x6bffffff, 0x6a000000, 0x40000000, 0x41ffffff, 0x43ffffff, 0x42000000, 0x47ffffff, 0x46000000, 0x44000000, 0x45ffffff, 0x4fffffff, 0x4e000000, 0x4c000000, 0x4dffffff, 0x48000000, 0x49ffffff, 0x4bffffff, 0x4a000000, 0x5fffffff, 0x5e000000, 0x5c000000, 0x5dffffff, 0x58000000, 0x59ffffff, 0x5bffffff, 0x5a000000, 0x50000000, 0x51ffffff, 0x53ffffff, 0x52000000, 0x57ffffff, 0x56000000, 0x54000000, 0x55ffffff, 0xffffffff, 0xfe000000, 0xfc000000, 0xfdffffff, 0xf8000000, 0xf9ffffff, 0xfbffffff, 0xfa000000, 0xf0000000, 0xf1ffffff, 0xf3ffffff, 0xf2000000, 0xf7ffffff, 0xf6000000, 0xf4000000, 0xf5ffffff, 0xe0000000, 0xe1ffffff, 0xe3ffffff, 0xe2000000, 0xe7ffffff, 0xe6000000, 0xe4000000, 0xe5ffffff, 0xefffffff, 0xee000000, 0xec000000, 0xedffffff, 0xe8000000, 0xe9ffffff, 0xebffffff, 0xea000000, 0xc0000000, 0xc1ffffff, 0xc3ffffff, 0xc2000000, 0xc7ffffff, 0xc6000000, 0xc4000000, 0xc5ffffff, 0xcfffffff, 0xce000000, 0xcc000000, 0xcdffffff, 0xc8000000, 0xc9ffffff, 0xcbffffff, 0xca000000, 0xdfffffff, 0xde000000, 0xdc000000, 0xddffffff, 0xd8000000, 0xd9ffffff, 0xdbffffff, 0xda000000, 0xd0000000, 0xd1ffffff, 0xd3ffffff, 0xd2000000, 0xd7ffffff, 0xd6000000, 0xd4000000, 0xd5ffffff, 0x80000000, 0x81ffffff, 0x83ffffff, 0x82000000, 0x87ffffff, 0x86000000, 0x84000000, 0x85ffffff, 0x8fffffff, 0x8e000000, 0x8c000000, 0x8dffffff, 0x88000000, 0x89ffffff, 0x8bffffff, 0x8a000000, 0x9fffffff, 0x9e000000, 0x9c000000, 0x9dffffff, 0x98000000, 0x99ffffff, 0x9bffffff, 0x9a000000, 0x90000000, 0x91ffffff, 0x93ffffff, 0x92000000, 0x97ffffff, 0x96000000, 0x94000000, 0x95ffffff, 0xbfffffff, 0xbe000000, 0xbc000000, 0xbdffffff, 0xb8000000, 0xb9ffffff, 0xbbffffff, 0xba000000, 0xb0000000, 0xb1ffffff, 0xb3ffffff, 0xb2000000, 0xb7ffffff, 0xb6000000, 0xb4000000, 0xb5ffffff, 0xa0000000, 0xa1ffffff, 0xa3ffffff, 0xa2000000, 0xa7ffffff, 0xa6000000, 0xa4000000, 0xa5ffffff, 0xafffffff, 0xae000000, 0xac000000, 0xadffffff, 0xa8000000, 0xa9ffffff, 0xabffffff, 0xaa000000};
static const uint32_t LookupTable_secondword[256] = { 0x0, 0x1ffff, 0x3ffff, 0x20000, 0x7ffff, 0x60000, 0x40000, 0x5ffff, 0xfffff, 0xe0000, 0xc0000, 0xdffff, 0x80000, 0x9ffff, 0xbffff, 0xa0000, 0x1fffff, 0x1e0000, 0x1c0000, 0x1dffff, 0x180000, 0x19ffff, 0x1bffff, 0x1a0000, 0x100000, 0x11ffff, 0x13ffff, 0x120000, 0x17ffff, 0x160000, 0x140000, 0x15ffff, 0x3fffff, 0x3e0000, 0x3c0000, 0x3dffff, 0x380000, 0x39ffff, 0x3bffff, 0x3a0000, 0x300000, 0x31ffff, 0x33ffff, 0x320000, 0x37ffff, 0x360000, 0x340000, 0x35ffff, 0x200000, 0x21ffff, 0x23ffff, 0x220000, 0x27ffff, 0x260000, 0x240000, 0x25ffff, 0x2fffff, 0x2e0000, 0x2c0000, 0x2dffff, 0x280000, 0x29ffff, 0x2bffff, 0x2a0000, 0x7fffff, 0x7e0000, 0x7c0000, 0x7dffff, 0x780000, 0x79ffff, 0x7bffff, 0x7a0000, 0x700000, 0x71ffff, 0x73ffff, 0x720000, 0x77ffff, 0x760000, 0x740000, 0x75ffff, 0x600000, 0x61ffff, 0x63ffff, 0x620000, 0x67ffff, 0x660000, 0x640000, 0x65ffff, 0x6fffff, 0x6e0000, 0x6c0000, 0x6dffff, 0x680000, 0x69ffff, 0x6bffff, 0x6a0000, 0x400000, 0x41ffff, 0x43ffff, 0x420000, 0x47ffff, 0x460000, 0x440000, 0x45ffff, 0x4fffff, 0x4e0000, 0x4c0000, 0x4dffff, 0x480000, 0x49ffff, 0x4bffff, 0x4a0000, 0x5fffff, 0x5e0000, 0x5c0000, 0x5dffff, 0x580000, 0x59ffff, 0x5bffff, 0x5a0000, 0x500000, 0x51ffff, 0x53ffff, 0x520000, 0x57ffff, 0x560000, 0x540000, 0x55ffff, 0xffffff, 0xfe0000, 0xfc0000, 0xfdffff, 0xf80000, 0xf9ffff, 0xfbffff, 0xfa0000, 0xf00000, 0xf1ffff, 0xf3ffff, 0xf20000, 0xf7ffff, 0xf60000, 0xf40000, 0xf5ffff, 0xe00000, 0xe1ffff, 0xe3ffff, 0xe20000, 0xe7ffff, 0xe60000, 0xe40000, 0xe5ffff, 0xefffff, 0xee0000, 0xec0000, 0xedffff, 0xe80000, 0xe9ffff, 0xebffff, 0xea0000, 0xc00000, 0xc1ffff, 0xc3ffff, 0xc20000, 0xc7ffff, 0xc60000, 0xc40000, 0xc5ffff, 0xcfffff, 0xce0000, 0xcc0000, 0xcdffff, 0xc80000, 0xc9ffff, 0xcbffff, 0xca0000, 0xdfffff, 0xde0000, 0xdc0000, 0xddffff, 0xd80000, 0xd9ffff, 0xdbffff, 0xda0000, 0xd00000, 0xd1ffff, 0xd3ffff, 0xd20000, 0xd7ffff, 0xd60000, 0xd40000, 0xd5ffff, 0x800000, 0x81ffff, 0x83ffff, 0x820000, 0x87ffff, 0x860000, 0x840000, 0x85ffff, 0x8fffff, 0x8e0000, 0x8c0000, 0x8dffff, 0x880000, 0x89ffff, 0x8bffff, 0x8a0000, 0x9fffff, 0x9e0000, 0x9c0000, 0x9dffff, 0x980000, 0x99ffff, 0x9bffff, 0x9a0000, 0x900000, 0x91ffff, 0x93ffff, 0x920000, 0x97ffff, 0x960000, 0x940000, 0x95ffff, 0xbfffff, 0xbe0000, 0xbc0000, 0xbdffff, 0xb80000, 0xb9ffff, 0xbbffff, 0xba0000, 0xb00000, 0xb1ffff, 0xb3ffff, 0xb20000, 0xb7ffff, 0xb60000, 0xb40000, 0xb5ffff, 0xa00000, 0xa1ffff, 0xa3ffff, 0xa20000, 0xa7ffff, 0xa60000, 0xa40000, 0xa5ffff, 0xafffff, 0xae0000, 0xac0000, 0xadffff, 0xa80000, 0xa9ffff, 0xabffff, 0xaa0000};
static const uint32_t LookupTable_thirdword[256]  = { 0x0, 0x1ff, 0x3ff, 0x200, 0x7ff, 0x600, 0x400, 0x5ff, 0xfff, 0xe00, 0xc00, 0xdff, 0x800, 0x9ff, 0xbff, 0xa00, 0x1fff, 0x1e00, 0x1c00, 0x1dff, 0x1800, 0x19ff, 0x1bff, 0x1a00, 0x1000, 0x11ff, 0x13ff, 0x1200, 0x17ff, 0x1600, 0x1400, 0x15ff, 0x3fff, 0x3e00, 0x3c00, 0x3dff, 0x3800, 0x39ff, 0x3bff, 0x3a00, 0x3000, 0x31ff, 0x33ff, 0x3200, 0x37ff, 0x3600, 0x3400, 0x35ff, 0x2000, 0x21ff, 0x23ff, 0x2200, 0x27ff, 0x2600, 0x2400, 0x25ff, 0x2fff, 0x2e00, 0x2c00, 0x2dff, 0x2800, 0x29ff, 0x2bff, 0x2a00, 0x7fff, 0x7e00, 0x7c00, 0x7dff, 0x7800, 0x79ff, 0x7bff, 0x7a00, 0x7000, 0x71ff, 0x73ff, 0x7200, 0x77ff, 0x7600, 0x7400, 0x75ff, 0x6000, 0x61ff, 0x63ff, 0x6200, 0x67ff, 0x6600, 0x6400, 0x65ff, 0x6fff, 0x6e00, 0x6c00, 0x6dff, 0x6800, 0x69ff, 0x6bff, 0x6a00, 0x4000, 0x41ff, 0x43ff, 0x4200, 0x47ff, 0x4600, 0x4400, 0x45ff, 0x4fff, 0x4e00, 0x4c00, 0x4dff, 0x4800, 0x49ff, 0x4bff, 0x4a00, 0x5fff, 0x5e00, 0x5c00, 0x5dff, 0x5800, 0x59ff, 0x5bff, 0x5a00, 0x5000, 0x51ff, 0x53ff, 0x5200, 0x57ff, 0x5600, 0x5400, 0x55ff, 0xffff, 0xfe00, 0xfc00, 0xfdff, 0xf800, 0xf9ff, 0xfbff, 0xfa00, 0xf000, 0xf1ff, 0xf3ff, 0xf200, 0xf7ff, 0xf600, 0xf400, 0xf5ff, 0xe000, 0xe1ff, 0xe3ff, 0xe200, 0xe7ff, 0xe600, 0xe400, 0xe5ff, 0xefff, 0xee00, 0xec00, 0xedff, 0xe800, 0xe9ff, 0xebff, 0xea00, 0xc000, 0xc1ff, 0xc3ff, 0xc200, 0xc7ff, 0xc600, 0xc400, 0xc5ff, 0xcfff, 0xce00, 0xcc00, 0xcdff, 0xc800, 0xc9ff, 0xcbff, 0xca00, 0xdfff, 0xde00, 0xdc00, 0xddff, 0xd800, 0xd9ff, 0xdbff, 0xda00, 0xd000, 0xd1ff, 0xd3ff, 0xd200, 0xd7ff, 0xd600, 0xd400, 0xd5ff, 0x8000, 0x81ff, 0x83ff, 0x8200, 0x87ff, 0x8600, 0x8400, 0x85ff, 0x8fff, 0x8e00, 0x8c00, 0x8dff, 0x8800, 0x89ff, 0x8bff, 0x8a00, 0x9fff, 0x9e00, 0x9c00, 0x9dff, 0x9800, 0x99ff, 0x9bff, 0x9a00, 0x9000, 0x91ff, 0x93ff, 0x9200, 0x97ff, 0x9600, 0x9400, 0x95ff, 0xbfff, 0xbe00, 0xbc00, 0xbdff, 0xb800, 0xb9ff, 0xbbff, 0xba00, 0xb000, 0xb1ff, 0xb3ff, 0xb200, 0xb7ff, 0xb600, 0xb400, 0xb5ff, 0xa000, 0xa1ff, 0xa3ff, 0xa200, 0xa7ff, 0xa600, 0xa400, 0xa5ff, 0xafff, 0xae00, 0xac00, 0xadff, 0xa800, 0xa9ff, 0xabff, 0xaa00};
static const uint32_t LookupTable_fourthword[256] = { 0x0, 0x1, 0x3, 0x2, 0x7, 0x6, 0x4, 0x5, 0xf, 0xe, 0xc, 0xd, 0x8, 0x9, 0xb, 0xa, 0x1f, 0x1e, 0x1c, 0x1d, 0x18, 0x19, 0x1b, 0x1a, 0x10, 0x11, 0x13, 0x12, 0x17, 0x16, 0x14, 0x15, 0x3f, 0x3e, 0x3c, 0x3d, 0x38, 0x39, 0x3b, 0x3a, 0x30, 0x31, 0x33, 0x32, 0x37, 0x36, 0x34, 0x35, 0x20, 0x21, 0x23, 0x22, 0x27, 0x26, 0x24, 0x25, 0x2f, 0x2e, 0x2c, 0x2d, 0x28, 0x29, 0x2b, 0x2a, 0x7f, 0x7e, 0x7c, 0x7d, 0x78, 0x79, 0x7b, 0x7a, 0x70, 0x71, 0x73, 0x72, 0x77, 0x76, 0x74, 0x75, 0x60, 0x61, 0x63, 0x62, 0x67, 0x66, 0x64, 0x65, 0x6f, 0x6e, 0x6c, 0x6d, 0x68, 0x69, 0x6b, 0x6a, 0x40, 0x41, 0x43, 0x42, 0x47, 0x46, 0x44, 0x45, 0x4f, 0x4e, 0x4c, 0x4d, 0x48, 0x49, 0x4b, 0x4a, 0x5f, 0x5e, 0x5c, 0x5d, 0x58, 0x59, 0x5b, 0x5a, 0x50, 0x51, 0x53, 0x52, 0x57, 0x56, 0x54, 0x55, 0xff, 0xfe, 0xfc, 0xfd, 0xf8, 0xf9, 0xfb, 0xfa, 0xf0, 0xf1, 0xf3, 0xf2, 0xf7, 0xf6, 0xf4, 0xf5, 0xe0, 0xe1, 0xe3, 0xe2, 0xe7, 0xe6, 0xe4, 0xe5, 0xef, 0xee, 0xec, 0xed, 0xe8, 0xe9, 0xeb, 0xea, 0xc0, 0xc1, 0xc3, 0xc2, 0xc7, 0xc6, 0xc4, 0xc5, 0xcf, 0xce, 0xcc, 0xcd, 0xc8, 0xc9, 0xcb, 0xca, 0xdf, 0xde, 0xdc, 0xdd, 0xd8, 0xd9, 0xdb, 0xda, 0xd0, 0xd1, 0xd3, 0xd2, 0xd7, 0xd6, 0xd4, 0xd5, 0x80, 0x81, 0x83, 0x82, 0x87, 0x86, 0x84, 0x85, 0x8f, 0x8e, 0x8c, 0x8d, 0x88, 0x89, 0x8b, 0x8a, 0x9f, 0x9e, 0x9c, 0x9d, 0x98, 0x99, 0x9b, 0x9a, 0x90, 0x91, 0x93, 0x92, 0x97, 0x96, 0x94, 0x95, 0xbf, 0xbe, 0xbc, 0xbd, 0xb8, 0xb9, 0xbb, 0xba, 0xb0, 0xb1, 0xb3, 0xb2, 0xb7, 0xb6, 0xb4, 0xb5, 0xa0, 0xa1, 0xa3, 0xa2, 0xa7, 0xa6, 0xa4, 0xa5, 0xaf, 0xae, 0xac, 0xad, 0xa8, 0xa9, 0xab, 0xaa};

void AudioOutputADAT::begin(void)
{

  dma.begin(true); // Allocate the DMA channel first

  block_ch1_1st = NULL;
  block_ch2_1st = NULL;
  block_ch3_1st = NULL;
  block_ch4_1st = NULL;
  block_ch5_1st = NULL;
  block_ch6_1st = NULL;
  block_ch7_1st = NULL;
  block_ch8_1st = NULL;
  // TODO: should we set & clear the I2S_TCSR_SR bit here?
  config_ADAT();
#if defined(KINETISK)
  CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0

  const int nbytes_mlno = 2 * 8; // 16 Bytes per minor loop

  dma.TCD->SADDR = ADAT_tx_buffer;
  dma.TCD->SOFF = 4;
  dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); //transfersize 2 = 32 bit, 5 = 32 byte
  dma.TCD->NBYTES_MLNO = nbytes_mlno;
  dma.TCD->SLAST = -sizeof(ADAT_tx_buffer);
  dma.TCD->DADDR = &I2S0_TDR0;
  dma.TCD->DOFF = 0;
  dma.TCD->CITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
  dma.TCD->DLASTSGA = 0;
  dma.TCD->BITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
  dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
  update_responsibility = update_setup();
  dma.enable();

  I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
  dma.attachInterrupt(isr);
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1052__)
  CORE_PIN6_CONFIG  = 3;  //1:TX_DATA0
#elif defined(__IMXRT1062__)
  CORE_PIN7_CONFIG  = 3;  //1:TX_DATA0
#endif

  const int nbytes_mlno = 2 * 8; // 16 Bytes per minor loop

  dma.TCD->SADDR = ADAT_tx_buffer;
  dma.TCD->SOFF = 4;
  dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  dma.TCD->NBYTES_MLNO = nbytes_mlno;
  dma.TCD->SLAST = -sizeof(ADAT_tx_buffer);
  dma.TCD->DADDR = &I2S1_TDR0; // TEENSY 4.x I2S1
  dma.TCD->DOFF = 0;
  dma.TCD->CITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
  dma.TCD->DLASTSGA = 0;
  dma.TCD->BITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
  dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); // TEENSY 4.x I2S1
  update_responsibility = update_setup();
  dma.enable();

  I2S1_RCSR |= I2S_RCSR_RE; // todo: yes we need this but why ?
  I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
  dma.attachInterrupt(isr);
#endif
}

/*

  https://ackspace.nl/wiki/ADAT_project
  https://github.com/xcore/sc_adat/blob/master/module_adat_tx/src/adat_tx_port.xc

  for information about the clock:

  http://cache.freescale.com/files/32bit/doc/app_note/AN4800.pdf

  We need a bitrate twice as high as the SPDIF example.
  Because BITCLK can not be the same as MCLK, but only half of MCLK, we need a 2*MCLK (so for 44100 samplerate we need 88200 MCLK)
*/

void AudioOutputADAT::isr(void)
{
  const int16_t *src1, *src2, *src3, *src4, *src5, *src6, *src7, *src8;
  const int16_t *zeros = (const int16_t *)zerodata;

  uint32_t *end, *dest;
  uint32_t saddr;
  uint32_t sample1, sample2, sample3, sample4, sample5, sample6, sample7, sample8;

  static uint32_t previousnrzi_highlow = 0; //this is used for the NZRI encoding to remember the last state.
  // if the result of the lookup table LSB is other than this lastbit, the result of the lookup table must be inverted.

  //static bool previousframeinverted=false;

  saddr = (uint32_t)(dma.TCD->SADDR);
  dma.clearInterrupt();
  if (saddr < (uint32_t)ADAT_tx_buffer + sizeof(ADAT_tx_buffer) / 2) {
    // DMA is transmitting the first half of the buffer
    // so we must fill the second half
    dest = (uint32_t *)&ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8 / 2];
    end = (uint32_t *)&ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8];
    if (AudioOutputADAT::update_responsibility) AudioStream::update_all();
  } else {
    // DMA is transmitting the second half of the buffer
    // so we must fill the first half
    dest = (uint32_t *)ADAT_tx_buffer;
    end = (uint32_t *)&ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8 / 2];
  }
#if IMXRT_CACHE_ENABLED >= 2
  uint32_t *toFlush = dest;
  uint32_t flushLen = sizeof ADAT_tx_buffer / 2;
#endif

  src1 = (block_ch1_1st) ? block_ch1_1st->data + ch1_offset : zeros;
  src2 = (block_ch2_1st) ? block_ch2_1st->data + ch2_offset : zeros;
  src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros;
  src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros;
  src5 = (block_ch5_1st) ? block_ch5_1st->data + ch5_offset : zeros;
  src6 = (block_ch6_1st) ? block_ch6_1st->data + ch6_offset : zeros;
  src7 = (block_ch7_1st) ? block_ch7_1st->data + ch7_offset : zeros;
  src8 = (block_ch8_1st) ? block_ch8_1st->data + ch8_offset : zeros;


  //Non-NZRI encoded 'empty' ADAT Frame
  /*
    (dest+0) = 0b10000100001000010000100001000010; // bit 0-31
    (dest+1) = 0b00010000100001000010000100001000; // bit 32-63
    (dest+2) = 0b01000010000100001000010000100001; // bit 64-95
    (dest+3) = 0b00001000010000100001000010000100; // bit 96-127

    (dest+4) = 0b00100001000010000100001000010000; // bit 128-159
    (dest+5) = 0b10000100001000010000100001000010; // bit 160-191
    (dest+6) = 0b00010000100001000010000100001000; // bit 192-223
    (dest+7) = 0b01000010000100001000000000010000; // bit 224-255

    dest+=8;

  */

  /*
    //NZRI encoded 'empty' ADAT Frame

    (dest+0) = 0b11111000001111100000111110000011; // bit 0-31
    (dest+1) = 0b11100000111110000011111000001111; // bit 32-63
    (dest+2) = 0b10000011111000001111100000111110; // bit 64-95
    (dest+3) = 0b00001111100000111110000011111000; // bit 96-127

    (dest+4) = 0b00111110000011111000001111100000; // bit 128-159
    (dest+5) = 0b11111000001111100000111110000011; // bit 160-191
    (dest+6) = 0b11100000111110000011111000001111; // bit 192-223
    (dest+7) = 0b10000011111000001111111111100000; // bit 224-255

    dest+=8;

  */

  do
  {
    sample1 = (*src1++);
    sample2 = (*src2++);
    sample3 = (*src3++);
    sample4 = (*src4++);
    sample5 = (*src5++);
    sample6 = (*src6++);
    sample7 = (*src7++);
    sample8 = (*src8++);

    uint32_t value;
    uint32_t nzrivalue;

    value =         0b10000100001000010000100001000010 /* bit  0-31 */
                    //                b00000000000000001000000000000000 // start of 16 bit sample
                    | ((sample1 << 15) & 0b01111000000000000000000000000000)
                    | ((sample1 << 14) & 0b00000011110000000000000000000000)
                    | ((sample1 << 13) & 0b00000000000111100000000000000000)
                    | ((sample1 << 12) & 0b00000000000000001111000000000000)
                    | ((sample2 >> 15) & 0b00000000000000000000000000000001)
                    ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 0) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00010000100001000010000100001000 /* bit 32-63 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample2 << 17) & 0b11100000000000000000000000000000)
                   | ((sample2 << 16) & 0b00001111000000000000000000000000)
                   | ((sample2 << 15) & 0b00000000011110000000000000000000)
                   | ((sample2 << 14) & 0b00000000000000111100000000000000)
                   | ((sample3 >> 13) & 0b00000000000000000000000000000111)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 1) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;


    value =        0b01000010000100001000010000100001 /* bit 64-95 */
                   //                b00000000000000001000000000000000 // start of 16 bit sample
                   | ((sample3 << 19) & 0b10000000000000000000000000000000)
                   | ((sample3 << 18) & 0b00111100000000000000000000000000)
                   | ((sample3 << 17) & 0b00000001111000000000000000000000)
                   | ((sample3 << 16) & 0b00000000000011110000000000000000)
                   | ((sample4 >> 11) & 0b00000000000000000000000000011110)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 2) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00001000010000100001000010000100 /* bit 96-127 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample4 << 20) & 0b11110000000000000000000000000000)
                   | ((sample4 << 19) & 0b00000111100000000000000000000000)
                   | ((sample4 << 18) & 0b00000000001111000000000000000000)
                   | ((sample5 >> 9 ) & 0b00000000000000000000000001111000)
                   | ((sample5 >> 10) & 0b00000000000000000000000000000011)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 3) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00100001000010000100001000010000 /* bit 128-159 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample5 << 22) & 0b11000000000000000000000000000000)
                   | ((sample5 << 21) & 0b00011110000000000000000000000000)
                   | ((sample5 << 20) & 0b00000000111100000000000000000000)
                   | ((sample6 >> 7 ) & 0b00000000000000000000000111100000)
                   | ((sample6 >> 8 ) & 0b00000000000000000000000000001111)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 4) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b10000100001000010000100001000010 /* bit 160-191 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample6 << 23) & 0b01111000000000000000000000000000)
                   | ((sample6 << 22) & 0b00000011110000000000000000000000)
                   | ((sample7 >> 5 ) & 0b00000000000000000000011110000000)
                   | ((sample7 >> 6 ) & 0b00000000000000000000000000111100)
                   | ((sample7 >> 7 ) & 0b00000000000000000000000000000001)
                   ;

    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 5) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00010000100001000010000100001000 /* bit 192-223 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample7 << 25) & 0b11100000000000000000000000000000)
                   | ((sample7 << 24) & 0b00001111000000000000000000000000)
                   | ((sample8 >> 3 ) & 0b00000000000000000001111000000000)
                   | ((sample8 >> 4 ) & 0b00000000000000000000000011110000)
                   | ((sample8 >> 5 ) & 0b00000000000000000000000000000111)
                   ;

    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 6) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b01000010000100001000000000010000 /* bit 224-255 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample8 << 27) & 0b10000000000000000000000000000000)
                   | ((sample8 << 26) & 0b00111100000000000000000000000000)
                   ;

    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 7) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    dest += 8;
  } while (dest < end);

#if IMXRT_CACHE_ENABLED >= 2
  arm_dcache_flush_delete(toFlush, flushLen);
#endif // IMXRT_CACHE_ENABLED >= 2  

  /*
    block = AudioOutputADAT::block_ch1_1st;
    if (block) {
    offset = AudioOutputADAT::ch1_offset;
    src = &block->data[offset];
    do {

      sample = (uint32_t)*src++;

    } while (dest < end);
    offset += AUDIO_BLOCK_SAMPLES/2;
    if (offset < AUDIO_BLOCK_SAMPLES) {
      AudioOutputADAT::ch1_offset = offset;
    } else {
      AudioOutputADAT::ch1_offset = 0;
      AudioStream::release(block);
      AudioOutputADAT::block_ch1_1st = AudioOutputADAT::block_ch1_2nd;
      AudioOutputADAT::block_ch1_2nd = NULL;
    }
    } else {
    sample = 0;

    do {
      // *(dest+0) = 0b11111000001111100000111110000011; // bit 0-31
      // *(dest+1) = 0b11100000111110000011111000001111; // bit 32-63
      // *(dest+2) = 0b10000011111000001111100000111110; // bit 64-95
      // *(dest+3) = 0b00001111100000111110000011111000; // bit 96-127

      // *(dest+4) = 0b00111110000011111000001111100000; // bit 128-159
      // *(dest+5) = 0b11111000001111100000111110000011; // bit 160-191
      // *(dest+6) = 0b11100000111110000011111000001111; // bit 192-223
      // *(dest+7) = 0b10000011111000001111111111100000; // bit 224-255
      dest+=8;
    } while (dest < end);
    }


    dest -= AUDIO_BLOCK_SAMPLES * 8/2 - 8/2;
    block = AudioOutputADAT::block_ch2_1st;
    if (block) {
    offset = AudioOutputADAT::ch2_offset;
    src = &block->data[offset];

    do {
      sample = *src++;

      // *(dest+0) = 0b00111110000011111000001111100000; // bit 128-159
      // *(dest+1) = 0b11111000001111100000111110000011; // bit 160-191
      // *(dest+2) = 0b11100000111110000011111000001111; // bit 192-223
      // *(dest+3) = 0b10000011111000001111111111100000; // bit 224-255
      dest+=8;
    } while (dest < end);

    offset += AUDIO_BLOCK_SAMPLES/2;
    if (offset < AUDIO_BLOCK_SAMPLES) {
      AudioOutputADAT::ch2_offset = offset;
    } else {
      AudioOutputADAT::ch2_offset = 0;
      AudioStream::release(block);
      AudioOutputADAT::block_ch2_1st = AudioOutputADAT::block_ch2_2nd;
      AudioOutputADAT::block_ch2_2nd = NULL;
    }
    } else {
      // *(dest+0) = 0b00111110000011111000001111100000; // bit 128-159
      // *(dest+1) = 0b11111000001111100000111110000011; // bit 160-191
      // *(dest+2) = 0b11100000111110000011111000001111; // bit 192-223
      // *(dest+3) = 0b10000011111000001111111111100000; // bit 224-255
      dest+=8;
    }*/

  if (block_ch1_1st) {
    if (ch1_offset == 0) {
      ch1_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch1_offset = 0;
      release(block_ch1_1st);
      block_ch1_1st = block_ch1_2nd;
      block_ch1_2nd = NULL;
    }
  }
  if (block_ch2_1st) {
    if (ch2_offset == 0) {
      ch2_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch2_offset = 0;
      release(block_ch2_1st);
      block_ch2_1st = block_ch2_2nd;
      block_ch2_2nd = NULL;
    }
  }
  if (block_ch3_1st) {
    if (ch3_offset == 0) {
      ch3_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch3_offset = 0;
      release(block_ch3_1st);
      block_ch3_1st = block_ch3_2nd;
      block_ch3_2nd = NULL;
    }
  }
  if (block_ch4_1st) {
    if (ch4_offset == 0) {
      ch4_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch4_offset = 0;
      release(block_ch4_1st);
      block_ch4_1st = block_ch4_2nd;
      block_ch4_2nd = NULL;
    }
  }
  if (block_ch5_1st) {
    if (ch5_offset == 0) {
      ch5_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch5_offset = 0;
      release(block_ch5_1st);
      block_ch5_1st = block_ch5_2nd;
      block_ch5_2nd = NULL;
    }
  }
  if (block_ch6_1st) {
    if (ch6_offset == 0) {
      ch6_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch6_offset = 0;
      release(block_ch6_1st);
      block_ch6_1st = block_ch6_2nd;
      block_ch6_2nd = NULL;
    }
  }
  if (block_ch7_1st) {
    if (ch7_offset == 0) {
      ch7_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch7_offset = 0;
      release(block_ch7_1st);
      block_ch7_1st = block_ch7_2nd;
      block_ch7_2nd = NULL;
    }
  }
  if (block_ch8_1st) {
    if (ch8_offset == 0) {
      ch8_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch8_offset = 0;
      release(block_ch8_1st);
      block_ch8_1st = block_ch8_2nd;
      block_ch8_2nd = NULL;
    }
  }
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
  // if you dont have this you just get noise! - AJ, https://forum.pjrc.com/threads/60914-ADAT-white-noise-on-Teensy-4-0
  arm_dcache_flush_delete((void *)((uint32_t)dest - sizeof(ADAT_tx_buffer) / 2), sizeof(ADAT_tx_buffer) / 2 );
#endif
}

void AudioOutputADAT::mute_PCM(const bool mute)
{
  //vucp = mute?VUCP_INVALID:VUCP_VALID;
  //#TODO

  // The TMR causes the Transmit Data pin to be tri-stated for the length of each selected
  // word and the transmit FIFO is not read for masked words.
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
  // configure transmitter T 4.x
  // SAI Transmit Mask Register (TMR)
  I2S1_TMR = mute; // 1 mute - 0 unmute
#elif defined(KINETISK)
  // configure transmitter T 3.x
  I2S0_TMR = mute; // 1 mute - 0 unmute
#endif
}

void AudioOutputADAT::update(void)
{

  audio_block_t *block, *tmp;

  block = receiveReadOnly(0); // input 0 = channel 1
  if (block) {
    __disable_irq();
    if (block_ch1_1st == NULL) {
      block_ch1_1st = block;
      ch1_offset = 0;
      __enable_irq();
    } else if (block_ch1_2nd == NULL) {
      block_ch1_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch1_1st;
      block_ch1_1st = block_ch1_2nd;
      block_ch1_2nd = block;
      ch1_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(1); // input 1 = channel 2
  if (block) {
    __disable_irq();
    if (block_ch2_1st == NULL) {
      block_ch2_1st = block;
      ch2_offset = 0;
      __enable_irq();
    } else if (block_ch2_2nd == NULL) {
      block_ch2_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch2_1st;
      block_ch2_1st = block_ch2_2nd;
      block_ch2_2nd = block;
      ch2_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(2); // channel 3
  if (block) {
    __disable_irq();
    if (block_ch3_1st == NULL) {
      block_ch3_1st = block;
      ch3_offset = 0;
      __enable_irq();
    } else if (block_ch3_2nd == NULL) {
      block_ch3_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch3_1st;
      block_ch3_1st = block_ch3_2nd;
      block_ch3_2nd = block;
      ch3_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(3); // channel 4
  if (block) {
    __disable_irq();
    if (block_ch4_1st == NULL) {
      block_ch4_1st = block;
      ch4_offset = 0;
      __enable_irq();
    } else if (block_ch4_2nd == NULL) {
      block_ch4_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch4_1st;
      block_ch4_1st = block_ch4_2nd;
      block_ch4_2nd = block;
      ch4_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(4); // channel 5
  if (block) {
    __disable_irq();
    if (block_ch5_1st == NULL) {
      block_ch5_1st = block;
      ch5_offset = 0;
      __enable_irq();
    } else if (block_ch5_2nd == NULL) {
      block_ch5_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch5_1st;
      block_ch5_1st = block_ch5_2nd;
      block_ch5_2nd = block;
      ch5_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(5); // channel 6
  if (block) {
    __disable_irq();
    if (block_ch6_1st == NULL) {
      block_ch6_1st = block;
      ch6_offset = 0;
      __enable_irq();
    } else if (block_ch6_2nd == NULL) {
      block_ch6_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch6_1st;
      block_ch6_1st = block_ch6_2nd;
      block_ch6_2nd = block;
      ch6_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(6); // channel 7
  if (block) {
    __disable_irq();
    if (block_ch7_1st == NULL) {
      block_ch7_1st = block;
      ch7_offset = 0;
      __enable_irq();
    } else if (block_ch7_2nd == NULL) {
      block_ch7_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch7_1st;
      block_ch7_1st = block_ch7_2nd;
      block_ch7_2nd = block;
      ch7_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(7); // channel 8
  if (block) {
    __disable_irq();
    if (block_ch8_1st == NULL) {
      block_ch8_1st = block;
      ch8_offset = 0;
      __enable_irq();
    } else if (block_ch8_2nd == NULL) {
      block_ch8_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch8_1st;
      block_ch8_1st = block_ch8_2nd;
      block_ch8_2nd = block;
      ch8_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
}


#ifndef MCLK_SRC
#if F_CPU >= 20000000
#define MCLK_SRC  3  // the PLL
#else
#define MCLK_SRC  0  // system clock
#endif
#endif

#if defined(KINETISK)
// Teensy 3.x simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT::set_Freq(int _FREQ)
{
  // TODO: should we set & clear the I2S_TCSR_SR bit here?
  setI2SFreq(_FREQ * 2); // TODO; test this on T3.x
  // https://forum.pjrc.com/threads/42092-Audio-Sample-Rate-how-to-change-from-44100-Hz-(44117-65-Hz)-to-48000
}

void AudioOutputADAT::config_ADAT(void)
{
  SIM_SCGC6 |= SIM_SCGC6_I2S;
  SIM_SCGC7 |= SIM_SCGC7_DMA;
  SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

  // enable MCLK output
  I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
  //while (I2S0_MCR & I2S_MCR_DUF) ;
  //I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));

  AudioOutputADAT::setI2SFreq(88200); // (default)

  // configure transmitter
  I2S0_TMR = 0; // SAI Transmit Mask Register (TMR)
  I2S0_TCR1 = I2S_TCR1_TFW(1);  // watermark
  I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  I2S0_TCR3 = I2S_TCR3_TCE;

  //8 Words per Frame 32 Bit Word-Length -> 256 Bit Frame-Length, MSB First:
  I2S0_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
  I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

  I2S0_RCSR = 0;

#if 0
  // configure pin mux for 3 clock signals (debug only)
  CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
  CORE_PIN9_CONFIG  = PORT_PCR_MUX(6); // pin  9, PTC3, I2S0_TX_BCLK
  //  CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
#endif
}

/*

  https://forum.pjrc.com/threads/38753-Discussion-about-a-simple-way-to-change-the-sample-rate

*/

void AudioOutputADAT::setI2SFreq(int freq) {
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } tmclk;

  const int numfreqs = 14;
  const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int) 44117.64706 , 48000, 88200, (int) (44117.64706 * 2.0), 96000, 176400, (int) (44117.64706 * 4.0), 192000};

#if (F_PLL==16000000)
  const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
  const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
  const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
  const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
  const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==168000000)
  const tmclk clkArr[numfreqs] = {{32, 2625}, {21, 1250}, {64, 2625}, {21, 625}, {128, 2625}, {42, 625}, {8, 119}, {64, 875}, {84, 625}, {16, 119}, {128, 875}, {168, 625}, {32, 119}, {189, 646} };
#elif (F_PLL==180000000)
  const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
  const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
  const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
  const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#elif (F_PLL==256000000)
  // TODO: fix these...
  const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif

  for (int f = 0; f < numfreqs; f++) {
    if ( freq == samplefreqs[f] ) {
      while (I2S0_MCR & I2S_MCR_DUF) ;
      I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
      return;
    }
  }
}
#endif

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)

// Teensy 4.x simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT::set_Freq(int _FREQ)
{
  // TODO: should we set & clear the I2S_TCSR_SR bit here?
  setI2SFreq(_FREQ * 2); // for ADAT we need (MCLK @24,576,000Mhz 48000kHz) or (MCLK @22,579,200Mhz 44100kHz)
}

void AudioOutputADAT::config_ADAT(void)
{
  // Set this to double the audio samplerate for adat to work! - AJ
  int fs = AUDIO_SAMPLE_RATE_EXACT * 2; // (default)

  CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);

  // if either transmitter or receiver is enabled, do nothing /AAA from output_i2s
  if (I2S1_TCSR & I2S_TCSR_TE) return;
  if (I2S1_RCSR & I2S_RCSR_RE) return;

  AudioOutputADAT::setI2SFreq(fs); // set PLL4, CCM_ANALOG

  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);

  // clear SAI1_CLK register locations
  CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
               | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4

  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
               | CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
               | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f

  //Select SAI1_MCLK1
  IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
                    | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));

  //  int rsync = 1; // 1b - Synchronous with receiver
  int tsync = 0; // transmitter -- 0b - Asynchronous mode, 1b - Synchronous with receiver.

  // configure transmitter
  I2S1_TMR = 0; // SAI Transmit Mask Register (TMR)
  I2S1_TCR1 = I2S_TCR1_RFW(1);  //was 0, supposed to be: I2S_TCR1_TFW(n), Transmit FIFO Watermark
  I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  I2S1_TCR3 = I2S_TCR3_TCE; // Enable

  // i want.. 8 Words per Frame 32 Bit Word-Length -> 256 Bit Frame-Length, MSB First:
  I2S1_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
  I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

  // configure receiver, ADAT IN not implemented yet.
  I2S1_RCSR = 0; // disabled
  /*
    I2S1_RMR = 0;
    //I2S1_RCSR = (1<<25); //Reset
    //I2S1_RCR1 = I2S_RCR1_RFW(0);

      // should this be I2S_TCR4_FRSZ(7) as above? both 3 and 7 work in both places - AJ
    I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
    I2S1_RCR3 = I2S_RCR3_RCE;

    I2S1_RCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
    I2S1_RCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
  */

#if 0
  //debug only:
  CORE_PIN23_CONFIG = 3;  //1:MCLK  22,579,200Mhz--@44100kHz, 24,576,000Mhz--@48000kHz
  CORE_PIN21_CONFIG = 3;  //1:RX_BCLK 5.6 MHz
  CORE_PIN20_CONFIG = 3;  //1:RX_SYNC 44.1 KHz
  //  CORE_PIN6_CONFIG  = 3;  //1:TX_DATA0
  //  CORE_PIN7_CONFIG  = 3;  //1:RX_DATA0
#endif
}
#endif

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
void AudioOutputADAT::setI2SFreq(int freq) {  // PLL4, CCM_ANALOG
  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (freq * 256 * n1);
  double C = ((double)freq * 256 * n1 * n2) / 24000000;
  int c0 = C;
  int c2 = 10000;
  int c1 = C * c2 - (c0 * c2);

  // PLL frequency correction @88200
  if (freq == 88200) { // see debug
    c1 = c1 + 1;
  }

  // set PLL4 CCM_ANALOG_PLL_AUDIO
  set_audioClock(c0, c1, c2, true); // see \libraries\Audio\utility\imxrt_hw.ccp

  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
               | CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
               | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f

#if 0
  //debug only:
  Serial.printf("SetI2SFreq(%d)\n", freq);
  //Serial.print("Freq "),Serial.println(freq);
  Serial.print("c0 DIV   "), Serial.println(c0);
  Serial.print("c1 NUM   "), Serial.println(c1);
  Serial.print("c2 DENOM "), Serial.println(c2);
  Serial.print("n1 PRED/ "), Serial.println(n1);
  Serial.print("n2 PODF/ "), Serial.println(n2);
  Serial.println();

  // -- Freq 88200    for 44100kHz ADAT, MCLK1 22,579,125Mhz -- we need 22,579,200MHz
  // c0 DIV   30      -- OSC_24000000Mhz *(30 + (1055/10000)) = 722,532,000MHz
  // c1 NUM   1055    <--- issue
  // c2 DENOM 10000
  // n1 PRED/ 4       -- 722,532,000 / 4 = 180,633,000MHz
  // n2 PODF/ 8       -- 180,633,000 / 8 = 22,579,125
  // MCLK--22,579,125MHz

  // -- Freq 88200   for 44100kHz ADAT, MCLK1 22,579,200Mhz
  // c0 DIV   30     -- OSC_24000000Mhz *(30 + (1056/10000)) = 722,534,400MHz
  // c1 NUM   1056   <--- PLL frequency correction @88200
  // c2 DENOM 10000
  // n1 PRED/ 4      -- 722,534,400 / 4 = 180,633,600MHz
  // n2 PODF/ 8      -- 180,633,600 / 8 = 22,579,200
  // MCLK--22,579,200MHz -- good @88200

  // -- Freq 96000   for 48000kHz ADAT, MCLK1 24,576,000Mhz
  // c0 DIV   28     -- OSC_24000000Mhz *(28 + (6720/10000)) = 688,128,000
  // c1 NUM   6720
  // c2 DENOM 10000
  // n1 PRED/ 4      -- 688,128,000 / 4 = 172,032,000
  // n2 PODF/ 7      -- 172,032,000 / 7 = 24,576,000
  // MCLK-- 24,576,000MHz   good@48000

  // -- Freq 48000    for 48000kHz, MCLK1 12,288,000Mhz
  // c0 DIV   28      -- OSC_24000000Mhz *(28 + (6720/10000)) = 688,128,000MHz
  // c1 NUM   6720
  // c2 DENOM 10000
  // n1 PRED/ 4       -- 688,128,000 / 4 = 172,032,000MHz
  // n2 PODF/ 14      -- 172,032,000 / 14 = 12,288,000
  // MCLK--12,288,000MHz -- good @48000

  // -- Freq 44100    -- OSC_24000000Mhz *(28 + (2240/10000)) = 677,376,000
  // c0 DIV   28
  // c1 NUM   2240
  // c2 DENOM 10000
  // n1 PRED/ 4       -- 677,376,000 / 4  = 169,344,000
  // n2 PODF/ 15      -- 169,344,000 / 15 = 11,289,600MHz
  // MCLK--11,289,600MHz -- good @44100
#endif
}
#endif

#elif defined(KINETISL)
// Fix compile error on Teensy LC

void AudioOutputADAT::mute_PCM(const bool mute)
{
  // Teensy LC return
}

// simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT::set_Freq(int _FREQ)
{
  // Teensy LC return
}

void AudioOutputADAT::begin(void)
{
  // Teensy LC return
}

void AudioOutputADAT::update(void)
{

  audio_block_t *block;
  block = receiveReadOnly(0); // input 0 = ch1 channel
  if (block) release(block);
  block = receiveReadOnly(1); // input 1 = ch2 channel
  if (block) release(block);
  block = receiveReadOnly(2); // input 2 = ch3 channel
  if (block) release(block);
  block = receiveReadOnly(3); // input 3 = ch4 channel
  if (block) release(block);
  block = receiveReadOnly(4); // input 4 = ch5 channel
  if (block) release(block);
  block = receiveReadOnly(5); // input 5 = ch6 channel
  if (block) release(block);
  block = receiveReadOnly(6); // input 6 = ch7 channel
  if (block) release(block);
  block = receiveReadOnly(7); // input 7 = ch8 channel
  if (block) release(block);
}

#endif

output_adat.h
Code:
/* ADAT for Teensy 3.X
 * Copyright (c) 2017, Ernstjan Freriks,
 * Thanks to Frank Bösing & KPC & Paul Stoffregen!
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef output_ADAT_h_
#define output_ADAT_h_

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

class AudioOutputADAT : public AudioStream
{
public:
	AudioOutputADAT(void) : AudioStream(8, inputQueueArray) { begin(); }
	virtual void update(void);
	void begin(void);
	static void mute_PCM(const bool mute);
	void set_Freq(int freq); // change the samplerate of ADAT at runtime, 44100 or 48000 only!!!
protected:
	AudioOutputADAT(int dummy): AudioStream(8, inputQueueArray) {}
	static void config_ADAT(void);
	static audio_block_t *block_ch1_1st;
	static audio_block_t *block_ch2_1st;
	static audio_block_t *block_ch3_1st;
	static audio_block_t *block_ch4_1st;
	static audio_block_t *block_ch5_1st;
	static audio_block_t *block_ch6_1st;
	static audio_block_t *block_ch7_1st;
	static audio_block_t *block_ch8_1st;
	static bool update_responsibility;
	static DMAChannel dma;
	static void isr(void);
	static void setI2SFreq(int freq);
private:
	//static uint32_t vucp;
	static audio_block_t *block_ch1_2nd;
	static audio_block_t *block_ch2_2nd;
	static audio_block_t *block_ch3_2nd;
	static audio_block_t *block_ch4_2nd;
	static audio_block_t *block_ch5_2nd;
	static audio_block_t *block_ch6_2nd;
	static audio_block_t *block_ch7_2nd;
	static audio_block_t *block_ch8_2nd;
	static uint16_t ch1_offset;
	static uint16_t ch2_offset;
	static uint16_t ch3_offset;
	static uint16_t ch4_offset;
	static uint16_t ch5_offset;
	static uint16_t ch6_offset;
	static uint16_t ch7_offset;
	static uint16_t ch8_offset;
	audio_block_t *inputQueueArray[8];
};

#endif
:)
 
Well I've been hacking with this some more and I got this ADAT going on SAI3 (Teensy 4.x only), that's right the SAI3 used for Medium Quality Sound (MQS).:D Tx_Data0 out on Pin 31- EMC36,
There is also MCLK3 on Pin 30 - EMC_37 if needed, I'm thinking about using it as input slave for my project.
 
Now I've got 16 channels ADAT OUT using PLL5_VIDEO, AudioInputSPDIF3 or AsyncAudioInputSPDIF3 together with AudioOutputSPDIF3 -- Teensy 4.x only!!!! ;)
TODO: Test i2s

Modified PassThroughAsyncSpdif example.
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: manually edited code !!

// dither = false, noiseshaping = false, anti-aliasing attenuation=100dB, minimum half resampling filter length=20,
// maximum half resampling filter length=80
AsyncAudioInputSPDIF3    spdif_async1(false, false, 100, 20, 80); // OK
//AudioInputSPDIF3       spdif_async1;                            // OK

//AudioOutputI2S           i2s2; // NO WORK WITH ASYNC SPDIF? OUT

AudioOutputSPDIF3        spdifOut;
AudioOutputADAT          adat1; 
AudioOutputADAT3         adat2;

AudioEffectReverb        reverb1;

// ADAT SIA1 OUT 8CH
AudioConnection          patchCord1(spdif_async1, 0, adat1, 0);
AudioConnection          patchCord2(spdif_async1, 0, adat1, 2);
AudioConnection          patchCord3(spdif_async1, 0, adat1, 4);
AudioConnection          patchCord4(spdif_async1, 0, adat1, 6);
AudioConnection          patchCord5(spdif_async1, 1, adat1, 1);
AudioConnection          patchCord6(spdif_async1, 1, adat1, 3);
AudioConnection          patchCord7(spdif_async1, 1, adat1, 5);
AudioConnection          patchCord8(spdif_async1, 1, adat1, 7);

AudioConnection          patchCord9(spdif_async1, 0, reverb1, 0);
AudioConnection          patchCord20(reverb1, 0, adat2, 6); //        CH7 L

// ADAT SIA3 OUT 8CH
AudioConnection          patchCord1B(spdif_async1, 0, adat2, 0);   // CH1 L
AudioConnection          patchCord2B(spdif_async1, 0, adat2, 2);   // CH3 L
AudioConnection          patchCord3B(spdif_async1, 0, adat2, 4);   // CH5 L
//AudioConnection          patchCord4B(spdif_async1, 0, adat2, 6); // CH7 L  off see reverb1
AudioConnection          patchCord5B(spdif_async1, 1, adat2, 1);   // CH2 R
AudioConnection          patchCord6B(spdif_async1, 1, adat2, 3);   // CH4 R
AudioConnection          patchCord7B(spdif_async1, 1, adat2, 5);   // CH6 R
AudioConnection          patchCord8B(spdif_async1, 1, adat2, 7);   // CH8 R

// i2s2 TODO TEST
///AudioConnection          patchCord9(spdif_async1, 0, i2s2, 0);
///AudioConnection          patchCord10(spdif_async1, 1, i2s2, 1);

// SPDIF OUT
AudioConnection          patchCord11(spdif_async1, 0, spdifOut, 0);
AudioConnection          patchCord12(spdif_async1, 1, spdifOut, 1);

void setup() {
  // put your setup code here, to run once:
  AudioMemory(30);
  Serial.begin(57600);
  while (!Serial && millis() < 5000); // wait for Serial Monitor with a timeout.

  // Teensy 3.x - 4.x simple way to change the samplerate of ADAT at runtime.
  // NOTE: 44100(default) or 48000 ADAT Only 8ch, No ADAT SMUX II-IV yet.
  // for details see forum Thread: ADAT white noise on Teensy 4.0
  // https://forum.pjrc.com/threads/60914-ADAT-white-noise-on-Teensy-4-0/page3
  //
  //adat1.set_Freq(44100); //
  //adat1.set_Freq(48000); // !!! But be aware that not the entire lib will work. Not all parts of it support that!
  //adat1.mute_PCM(1); // 1 mute
  //adat1.mute_PCM(0); // 0 unmute
}

void loop() {
  /* // For AsyncAudioInput only!!!
    double bufferedTime = spdif_async1.getBufferedTime();
    Serial.print("buffered time [micro seconds]: ");
    Serial.println(bufferedTime * 1e6, 2);


    Serial.print("locked: ");
    Serial.println(spdif_async1.isLocked());
    Serial.print("input frequency: ");
    double inputFrequency = spdif_async1.getInputFrequency();
    Serial.println(inputFrequency);
    Serial.print("anti-aliasing attenuation: ");
    Serial.println(spdif_async1.getAttenuation());

    Serial.print("resampling goup delay [milli seconds]: ");
    Serial.println(spdif_async1.getHalfFilterLength() / inputFrequency * 1e3, 2);

    Serial.print("half filter length: ");
    Serial.println(spdif_async1.getHalfFilterLength());

    double pUsageIn = spdif_async1.processorUsage();
    Serial.print("processor usage [%]: ");
    Serial.println(pUsageIn);

    Serial.print("max number of used blocks: ");
    Serial.println(AudioMemoryUsageMax());

    delay(5000);
  */
}

Ignore the warnings in audio tool.
ADAT 16 OUT.jpg

ADAT SAI3 + PLL5_VIDEO
output_adat3.cpp
Code:
/* ADAT for Teensy 4.X
   Copyright (c) 2017, Ernstjan Freriks,
   Thanks to Frank Bösing & KPC & Paul Stoffregen!

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice, development funding notice, and this permission
   notice shall be included in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

// Chris O. 4/03/2023
// SAI3 ADAT + PLL5 VIDEO -- Teensy 4.x only !!!!
// output_adat3.cpp

#include <Arduino.h>
#include "output_adat3.h"
#include "utility/imxrt_hw.h"

/* Use PLL5_VIDEO - must be 1 */
#define ENABLE_PLL5_VIDEO 1

#if ENABLE_PLL5_VIDEO
#pragma message("Use PLL5_VIDEO - ADAT SAI3")
// P.1114, not in imxrt.h
#define CCM_ANALOG_PLL_VIDEO_NUM_MASK    (((1<<29)-1))
#define CCM_ANALOG_PLL_VIDEO_DENOM_MASK   (((1<<29)-1))
#else
#pragma message("Use PLL4_AUDIO")
#endif  // ENABLE_PLL5_VIDEO

#if defined(__IMXRT1062__)

audio_block_t * AudioOutputADAT3::block_ch1_1st = NULL;
audio_block_t * AudioOutputADAT3::block_ch2_1st = NULL;
audio_block_t * AudioOutputADAT3::block_ch3_1st = NULL;
audio_block_t * AudioOutputADAT3::block_ch4_1st = NULL;
audio_block_t * AudioOutputADAT3::block_ch5_1st = NULL;
audio_block_t * AudioOutputADAT3::block_ch6_1st = NULL;
audio_block_t * AudioOutputADAT3::block_ch7_1st = NULL;
audio_block_t * AudioOutputADAT3::block_ch8_1st = NULL;

audio_block_t * AudioOutputADAT3::block_ch1_2nd = NULL;
audio_block_t * AudioOutputADAT3::block_ch2_2nd = NULL;
audio_block_t * AudioOutputADAT3::block_ch3_2nd = NULL;
audio_block_t * AudioOutputADAT3::block_ch4_2nd = NULL;
audio_block_t * AudioOutputADAT3::block_ch5_2nd = NULL;
audio_block_t * AudioOutputADAT3::block_ch6_2nd = NULL;
audio_block_t * AudioOutputADAT3::block_ch7_2nd = NULL;
audio_block_t * AudioOutputADAT3::block_ch8_2nd = NULL;

uint16_t  AudioOutputADAT3::ch1_offset = 0;
uint16_t  AudioOutputADAT3::ch2_offset = 0;
uint16_t  AudioOutputADAT3::ch3_offset = 0;
uint16_t  AudioOutputADAT3::ch4_offset = 0;
uint16_t  AudioOutputADAT3::ch5_offset = 0;
uint16_t  AudioOutputADAT3::ch6_offset = 0;
uint16_t  AudioOutputADAT3::ch7_offset = 0;
uint16_t  AudioOutputADAT3::ch8_offset = 0;

bool AudioOutputADAT3::update_responsibility = false;

DMAMEM __attribute__((aligned(32))) static uint32_t ADAT3_tx_buffer[AUDIO_BLOCK_SAMPLES * 8]; //4 KB, AUDIO_BLOCK_SAMPLES is usually 128
DMAChannel AudioOutputADAT3::dma(false);

static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES / 4] = {0};

// These are the lookup tables. There are four of them so that the remainder of the 32 bit result can easily be XORred with the next 8 bits.
static const uint32_t LookupTable_firstword[256]  = { 0x0, 0x1ffffff, 0x3ffffff, 0x2000000, 0x7ffffff, 0x6000000, 0x4000000, 0x5ffffff, 0xfffffff, 0xe000000, 0xc000000, 0xdffffff, 0x8000000, 0x9ffffff, 0xbffffff, 0xa000000, 0x1fffffff, 0x1e000000, 0x1c000000, 0x1dffffff, 0x18000000, 0x19ffffff, 0x1bffffff, 0x1a000000, 0x10000000, 0x11ffffff, 0x13ffffff, 0x12000000, 0x17ffffff, 0x16000000, 0x14000000, 0x15ffffff, 0x3fffffff, 0x3e000000, 0x3c000000, 0x3dffffff, 0x38000000, 0x39ffffff, 0x3bffffff, 0x3a000000, 0x30000000, 0x31ffffff, 0x33ffffff, 0x32000000, 0x37ffffff, 0x36000000, 0x34000000, 0x35ffffff, 0x20000000, 0x21ffffff, 0x23ffffff, 0x22000000, 0x27ffffff, 0x26000000, 0x24000000, 0x25ffffff, 0x2fffffff, 0x2e000000, 0x2c000000, 0x2dffffff, 0x28000000, 0x29ffffff, 0x2bffffff, 0x2a000000, 0x7fffffff, 0x7e000000, 0x7c000000, 0x7dffffff, 0x78000000, 0x79ffffff, 0x7bffffff, 0x7a000000, 0x70000000, 0x71ffffff, 0x73ffffff, 0x72000000, 0x77ffffff, 0x76000000, 0x74000000, 0x75ffffff, 0x60000000, 0x61ffffff, 0x63ffffff, 0x62000000, 0x67ffffff, 0x66000000, 0x64000000, 0x65ffffff, 0x6fffffff, 0x6e000000, 0x6c000000, 0x6dffffff, 0x68000000, 0x69ffffff, 0x6bffffff, 0x6a000000, 0x40000000, 0x41ffffff, 0x43ffffff, 0x42000000, 0x47ffffff, 0x46000000, 0x44000000, 0x45ffffff, 0x4fffffff, 0x4e000000, 0x4c000000, 0x4dffffff, 0x48000000, 0x49ffffff, 0x4bffffff, 0x4a000000, 0x5fffffff, 0x5e000000, 0x5c000000, 0x5dffffff, 0x58000000, 0x59ffffff, 0x5bffffff, 0x5a000000, 0x50000000, 0x51ffffff, 0x53ffffff, 0x52000000, 0x57ffffff, 0x56000000, 0x54000000, 0x55ffffff, 0xffffffff, 0xfe000000, 0xfc000000, 0xfdffffff, 0xf8000000, 0xf9ffffff, 0xfbffffff, 0xfa000000, 0xf0000000, 0xf1ffffff, 0xf3ffffff, 0xf2000000, 0xf7ffffff, 0xf6000000, 0xf4000000, 0xf5ffffff, 0xe0000000, 0xe1ffffff, 0xe3ffffff, 0xe2000000, 0xe7ffffff, 0xe6000000, 0xe4000000, 0xe5ffffff, 0xefffffff, 0xee000000, 0xec000000, 0xedffffff, 0xe8000000, 0xe9ffffff, 0xebffffff, 0xea000000, 0xc0000000, 0xc1ffffff, 0xc3ffffff, 0xc2000000, 0xc7ffffff, 0xc6000000, 0xc4000000, 0xc5ffffff, 0xcfffffff, 0xce000000, 0xcc000000, 0xcdffffff, 0xc8000000, 0xc9ffffff, 0xcbffffff, 0xca000000, 0xdfffffff, 0xde000000, 0xdc000000, 0xddffffff, 0xd8000000, 0xd9ffffff, 0xdbffffff, 0xda000000, 0xd0000000, 0xd1ffffff, 0xd3ffffff, 0xd2000000, 0xd7ffffff, 0xd6000000, 0xd4000000, 0xd5ffffff, 0x80000000, 0x81ffffff, 0x83ffffff, 0x82000000, 0x87ffffff, 0x86000000, 0x84000000, 0x85ffffff, 0x8fffffff, 0x8e000000, 0x8c000000, 0x8dffffff, 0x88000000, 0x89ffffff, 0x8bffffff, 0x8a000000, 0x9fffffff, 0x9e000000, 0x9c000000, 0x9dffffff, 0x98000000, 0x99ffffff, 0x9bffffff, 0x9a000000, 0x90000000, 0x91ffffff, 0x93ffffff, 0x92000000, 0x97ffffff, 0x96000000, 0x94000000, 0x95ffffff, 0xbfffffff, 0xbe000000, 0xbc000000, 0xbdffffff, 0xb8000000, 0xb9ffffff, 0xbbffffff, 0xba000000, 0xb0000000, 0xb1ffffff, 0xb3ffffff, 0xb2000000, 0xb7ffffff, 0xb6000000, 0xb4000000, 0xb5ffffff, 0xa0000000, 0xa1ffffff, 0xa3ffffff, 0xa2000000, 0xa7ffffff, 0xa6000000, 0xa4000000, 0xa5ffffff, 0xafffffff, 0xae000000, 0xac000000, 0xadffffff, 0xa8000000, 0xa9ffffff, 0xabffffff, 0xaa000000};
static const uint32_t LookupTable_secondword[256] = { 0x0, 0x1ffff, 0x3ffff, 0x20000, 0x7ffff, 0x60000, 0x40000, 0x5ffff, 0xfffff, 0xe0000, 0xc0000, 0xdffff, 0x80000, 0x9ffff, 0xbffff, 0xa0000, 0x1fffff, 0x1e0000, 0x1c0000, 0x1dffff, 0x180000, 0x19ffff, 0x1bffff, 0x1a0000, 0x100000, 0x11ffff, 0x13ffff, 0x120000, 0x17ffff, 0x160000, 0x140000, 0x15ffff, 0x3fffff, 0x3e0000, 0x3c0000, 0x3dffff, 0x380000, 0x39ffff, 0x3bffff, 0x3a0000, 0x300000, 0x31ffff, 0x33ffff, 0x320000, 0x37ffff, 0x360000, 0x340000, 0x35ffff, 0x200000, 0x21ffff, 0x23ffff, 0x220000, 0x27ffff, 0x260000, 0x240000, 0x25ffff, 0x2fffff, 0x2e0000, 0x2c0000, 0x2dffff, 0x280000, 0x29ffff, 0x2bffff, 0x2a0000, 0x7fffff, 0x7e0000, 0x7c0000, 0x7dffff, 0x780000, 0x79ffff, 0x7bffff, 0x7a0000, 0x700000, 0x71ffff, 0x73ffff, 0x720000, 0x77ffff, 0x760000, 0x740000, 0x75ffff, 0x600000, 0x61ffff, 0x63ffff, 0x620000, 0x67ffff, 0x660000, 0x640000, 0x65ffff, 0x6fffff, 0x6e0000, 0x6c0000, 0x6dffff, 0x680000, 0x69ffff, 0x6bffff, 0x6a0000, 0x400000, 0x41ffff, 0x43ffff, 0x420000, 0x47ffff, 0x460000, 0x440000, 0x45ffff, 0x4fffff, 0x4e0000, 0x4c0000, 0x4dffff, 0x480000, 0x49ffff, 0x4bffff, 0x4a0000, 0x5fffff, 0x5e0000, 0x5c0000, 0x5dffff, 0x580000, 0x59ffff, 0x5bffff, 0x5a0000, 0x500000, 0x51ffff, 0x53ffff, 0x520000, 0x57ffff, 0x560000, 0x540000, 0x55ffff, 0xffffff, 0xfe0000, 0xfc0000, 0xfdffff, 0xf80000, 0xf9ffff, 0xfbffff, 0xfa0000, 0xf00000, 0xf1ffff, 0xf3ffff, 0xf20000, 0xf7ffff, 0xf60000, 0xf40000, 0xf5ffff, 0xe00000, 0xe1ffff, 0xe3ffff, 0xe20000, 0xe7ffff, 0xe60000, 0xe40000, 0xe5ffff, 0xefffff, 0xee0000, 0xec0000, 0xedffff, 0xe80000, 0xe9ffff, 0xebffff, 0xea0000, 0xc00000, 0xc1ffff, 0xc3ffff, 0xc20000, 0xc7ffff, 0xc60000, 0xc40000, 0xc5ffff, 0xcfffff, 0xce0000, 0xcc0000, 0xcdffff, 0xc80000, 0xc9ffff, 0xcbffff, 0xca0000, 0xdfffff, 0xde0000, 0xdc0000, 0xddffff, 0xd80000, 0xd9ffff, 0xdbffff, 0xda0000, 0xd00000, 0xd1ffff, 0xd3ffff, 0xd20000, 0xd7ffff, 0xd60000, 0xd40000, 0xd5ffff, 0x800000, 0x81ffff, 0x83ffff, 0x820000, 0x87ffff, 0x860000, 0x840000, 0x85ffff, 0x8fffff, 0x8e0000, 0x8c0000, 0x8dffff, 0x880000, 0x89ffff, 0x8bffff, 0x8a0000, 0x9fffff, 0x9e0000, 0x9c0000, 0x9dffff, 0x980000, 0x99ffff, 0x9bffff, 0x9a0000, 0x900000, 0x91ffff, 0x93ffff, 0x920000, 0x97ffff, 0x960000, 0x940000, 0x95ffff, 0xbfffff, 0xbe0000, 0xbc0000, 0xbdffff, 0xb80000, 0xb9ffff, 0xbbffff, 0xba0000, 0xb00000, 0xb1ffff, 0xb3ffff, 0xb20000, 0xb7ffff, 0xb60000, 0xb40000, 0xb5ffff, 0xa00000, 0xa1ffff, 0xa3ffff, 0xa20000, 0xa7ffff, 0xa60000, 0xa40000, 0xa5ffff, 0xafffff, 0xae0000, 0xac0000, 0xadffff, 0xa80000, 0xa9ffff, 0xabffff, 0xaa0000};
static const uint32_t LookupTable_thirdword[256]  = { 0x0, 0x1ff, 0x3ff, 0x200, 0x7ff, 0x600, 0x400, 0x5ff, 0xfff, 0xe00, 0xc00, 0xdff, 0x800, 0x9ff, 0xbff, 0xa00, 0x1fff, 0x1e00, 0x1c00, 0x1dff, 0x1800, 0x19ff, 0x1bff, 0x1a00, 0x1000, 0x11ff, 0x13ff, 0x1200, 0x17ff, 0x1600, 0x1400, 0x15ff, 0x3fff, 0x3e00, 0x3c00, 0x3dff, 0x3800, 0x39ff, 0x3bff, 0x3a00, 0x3000, 0x31ff, 0x33ff, 0x3200, 0x37ff, 0x3600, 0x3400, 0x35ff, 0x2000, 0x21ff, 0x23ff, 0x2200, 0x27ff, 0x2600, 0x2400, 0x25ff, 0x2fff, 0x2e00, 0x2c00, 0x2dff, 0x2800, 0x29ff, 0x2bff, 0x2a00, 0x7fff, 0x7e00, 0x7c00, 0x7dff, 0x7800, 0x79ff, 0x7bff, 0x7a00, 0x7000, 0x71ff, 0x73ff, 0x7200, 0x77ff, 0x7600, 0x7400, 0x75ff, 0x6000, 0x61ff, 0x63ff, 0x6200, 0x67ff, 0x6600, 0x6400, 0x65ff, 0x6fff, 0x6e00, 0x6c00, 0x6dff, 0x6800, 0x69ff, 0x6bff, 0x6a00, 0x4000, 0x41ff, 0x43ff, 0x4200, 0x47ff, 0x4600, 0x4400, 0x45ff, 0x4fff, 0x4e00, 0x4c00, 0x4dff, 0x4800, 0x49ff, 0x4bff, 0x4a00, 0x5fff, 0x5e00, 0x5c00, 0x5dff, 0x5800, 0x59ff, 0x5bff, 0x5a00, 0x5000, 0x51ff, 0x53ff, 0x5200, 0x57ff, 0x5600, 0x5400, 0x55ff, 0xffff, 0xfe00, 0xfc00, 0xfdff, 0xf800, 0xf9ff, 0xfbff, 0xfa00, 0xf000, 0xf1ff, 0xf3ff, 0xf200, 0xf7ff, 0xf600, 0xf400, 0xf5ff, 0xe000, 0xe1ff, 0xe3ff, 0xe200, 0xe7ff, 0xe600, 0xe400, 0xe5ff, 0xefff, 0xee00, 0xec00, 0xedff, 0xe800, 0xe9ff, 0xebff, 0xea00, 0xc000, 0xc1ff, 0xc3ff, 0xc200, 0xc7ff, 0xc600, 0xc400, 0xc5ff, 0xcfff, 0xce00, 0xcc00, 0xcdff, 0xc800, 0xc9ff, 0xcbff, 0xca00, 0xdfff, 0xde00, 0xdc00, 0xddff, 0xd800, 0xd9ff, 0xdbff, 0xda00, 0xd000, 0xd1ff, 0xd3ff, 0xd200, 0xd7ff, 0xd600, 0xd400, 0xd5ff, 0x8000, 0x81ff, 0x83ff, 0x8200, 0x87ff, 0x8600, 0x8400, 0x85ff, 0x8fff, 0x8e00, 0x8c00, 0x8dff, 0x8800, 0x89ff, 0x8bff, 0x8a00, 0x9fff, 0x9e00, 0x9c00, 0x9dff, 0x9800, 0x99ff, 0x9bff, 0x9a00, 0x9000, 0x91ff, 0x93ff, 0x9200, 0x97ff, 0x9600, 0x9400, 0x95ff, 0xbfff, 0xbe00, 0xbc00, 0xbdff, 0xb800, 0xb9ff, 0xbbff, 0xba00, 0xb000, 0xb1ff, 0xb3ff, 0xb200, 0xb7ff, 0xb600, 0xb400, 0xb5ff, 0xa000, 0xa1ff, 0xa3ff, 0xa200, 0xa7ff, 0xa600, 0xa400, 0xa5ff, 0xafff, 0xae00, 0xac00, 0xadff, 0xa800, 0xa9ff, 0xabff, 0xaa00};
static const uint32_t LookupTable_fourthword[256] = { 0x0, 0x1, 0x3, 0x2, 0x7, 0x6, 0x4, 0x5, 0xf, 0xe, 0xc, 0xd, 0x8, 0x9, 0xb, 0xa, 0x1f, 0x1e, 0x1c, 0x1d, 0x18, 0x19, 0x1b, 0x1a, 0x10, 0x11, 0x13, 0x12, 0x17, 0x16, 0x14, 0x15, 0x3f, 0x3e, 0x3c, 0x3d, 0x38, 0x39, 0x3b, 0x3a, 0x30, 0x31, 0x33, 0x32, 0x37, 0x36, 0x34, 0x35, 0x20, 0x21, 0x23, 0x22, 0x27, 0x26, 0x24, 0x25, 0x2f, 0x2e, 0x2c, 0x2d, 0x28, 0x29, 0x2b, 0x2a, 0x7f, 0x7e, 0x7c, 0x7d, 0x78, 0x79, 0x7b, 0x7a, 0x70, 0x71, 0x73, 0x72, 0x77, 0x76, 0x74, 0x75, 0x60, 0x61, 0x63, 0x62, 0x67, 0x66, 0x64, 0x65, 0x6f, 0x6e, 0x6c, 0x6d, 0x68, 0x69, 0x6b, 0x6a, 0x40, 0x41, 0x43, 0x42, 0x47, 0x46, 0x44, 0x45, 0x4f, 0x4e, 0x4c, 0x4d, 0x48, 0x49, 0x4b, 0x4a, 0x5f, 0x5e, 0x5c, 0x5d, 0x58, 0x59, 0x5b, 0x5a, 0x50, 0x51, 0x53, 0x52, 0x57, 0x56, 0x54, 0x55, 0xff, 0xfe, 0xfc, 0xfd, 0xf8, 0xf9, 0xfb, 0xfa, 0xf0, 0xf1, 0xf3, 0xf2, 0xf7, 0xf6, 0xf4, 0xf5, 0xe0, 0xe1, 0xe3, 0xe2, 0xe7, 0xe6, 0xe4, 0xe5, 0xef, 0xee, 0xec, 0xed, 0xe8, 0xe9, 0xeb, 0xea, 0xc0, 0xc1, 0xc3, 0xc2, 0xc7, 0xc6, 0xc4, 0xc5, 0xcf, 0xce, 0xcc, 0xcd, 0xc8, 0xc9, 0xcb, 0xca, 0xdf, 0xde, 0xdc, 0xdd, 0xd8, 0xd9, 0xdb, 0xda, 0xd0, 0xd1, 0xd3, 0xd2, 0xd7, 0xd6, 0xd4, 0xd5, 0x80, 0x81, 0x83, 0x82, 0x87, 0x86, 0x84, 0x85, 0x8f, 0x8e, 0x8c, 0x8d, 0x88, 0x89, 0x8b, 0x8a, 0x9f, 0x9e, 0x9c, 0x9d, 0x98, 0x99, 0x9b, 0x9a, 0x90, 0x91, 0x93, 0x92, 0x97, 0x96, 0x94, 0x95, 0xbf, 0xbe, 0xbc, 0xbd, 0xb8, 0xb9, 0xbb, 0xba, 0xb0, 0xb1, 0xb3, 0xb2, 0xb7, 0xb6, 0xb4, 0xb5, 0xa0, 0xa1, 0xa3, 0xa2, 0xa7, 0xa6, 0xa4, 0xa5, 0xaf, 0xae, 0xac, 0xad, 0xa8, 0xa9, 0xab, 0xaa};

void AudioOutputADAT3::begin(void)
{

  dma.begin(true); // Allocate the DMA channel first

  block_ch1_1st = NULL;
  block_ch2_1st = NULL;
  block_ch3_1st = NULL;
  block_ch4_1st = NULL;
  block_ch5_1st = NULL;
  block_ch6_1st = NULL;
  block_ch7_1st = NULL;
  block_ch8_1st = NULL;
  // TODO: should we set & clear the I2S_TCSR_SR bit here?
  config_ADAT();

  //#if defined(__IMXRT1062__)
  CORE_PIN31_CONFIG  = 3;  //1:SAI3_TX_DATA0  PIN 31 - EMC_36
  //#endif

  const int nbytes_mlno = 2 * 8; // 16 Bytes per minor loop

  dma.TCD->SADDR = ADAT3_tx_buffer;
  dma.TCD->SOFF = 4;
  dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  dma.TCD->NBYTES_MLNO = nbytes_mlno; // 16
  dma.TCD->SLAST = -sizeof(ADAT3_tx_buffer);
  //dma.TCD->DADDR = &I2S1_TDR0; // TEENSY 4.x I2S1
  dma.TCD->DADDR = &I2S3_TDR0; // TEENSY 4.x I2S3  TX_DATA0 - PIN31, EMC36
  dma.TCD->DOFF = 0;
  dma.TCD->CITER_ELINKNO = sizeof(ADAT3_tx_buffer) / nbytes_mlno;
  dma.TCD->DLASTSGA = 0;
  dma.TCD->BITER_ELINKNO = sizeof(ADAT3_tx_buffer) / nbytes_mlno;
  dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  //dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); // TEENSY 4.x I2S1
  dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI3_TX); // TEENSY 4.x SAI3
  update_responsibility = update_setup();
  dma.enable();

  // I2S3_RCSR |= I2S_RCSR_RE; // I2S3    DO WE  ??  todo: yes we need this but why ? ------------------------------------<<<< SEE LINE 606
  I2S3_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; // I2S3
  dma.attachInterrupt(isr);
}

/*

  https://ackspace.nl/wiki/ADAT_project
  https://github.com/xcore/sc_adat/blob/master/module_adat_tx/src/adat_tx_port.xc

  for information about the clock:

  http://cache.freescale.com/files/32bit/doc/app_note/AN4800.pdf

  We need a bitrate twice as high as the SPDIF example.
  Because BITCLK can not be the same as MCLK, but only half of MCLK, we need a 2*MCLK (so for 44100 samplerate we need 88200 MCLK)
*/

void AudioOutputADAT3::isr(void)
{
  const int16_t *src1, *src2, *src3, *src4, *src5, *src6, *src7, *src8;
  const int16_t *zeros = (const int16_t *)zerodata;

  uint32_t *end, *dest;
  uint32_t saddr;
  uint32_t sample1, sample2, sample3, sample4, sample5, sample6, sample7, sample8;

  static uint32_t previousnrzi_highlow = 0; //this is used for the NZRI encoding to remember the last state.
  // if the result of the lookup table LSB is other than this lastbit, the result of the lookup table must be inverted.

  //static bool previousframeinverted=false;

  saddr = (uint32_t)(dma.TCD->SADDR);
  dma.clearInterrupt();
  if (saddr < (uint32_t)ADAT3_tx_buffer + sizeof(ADAT3_tx_buffer) / 2) {
    // DMA is transmitting the first half of the buffer
    // so we must fill the second half
    dest = (uint32_t *)&ADAT3_tx_buffer[AUDIO_BLOCK_SAMPLES * 8 / 2];
    end = (uint32_t *)&ADAT3_tx_buffer[AUDIO_BLOCK_SAMPLES * 8];
    if (AudioOutputADAT3::update_responsibility) AudioStream::update_all();
  } else {
    // DMA is transmitting the second half of the buffer
    // so we must fill the first half
    dest = (uint32_t *)ADAT3_tx_buffer;
    end = (uint32_t *)&ADAT3_tx_buffer[AUDIO_BLOCK_SAMPLES * 8 / 2];
  }

#if IMXRT_CACHE_ENABLED >= 2
  uint32_t *toFlush = dest;
  uint32_t flushLen = sizeof ADAT3_tx_buffer / 2;
#endif

  src1 = (block_ch1_1st) ? block_ch1_1st->data + ch1_offset : zeros;
  src2 = (block_ch2_1st) ? block_ch2_1st->data + ch2_offset : zeros;
  src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros;
  src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros;
  src5 = (block_ch5_1st) ? block_ch5_1st->data + ch5_offset : zeros;
  src6 = (block_ch6_1st) ? block_ch6_1st->data + ch6_offset : zeros;
  src7 = (block_ch7_1st) ? block_ch7_1st->data + ch7_offset : zeros;
  src8 = (block_ch8_1st) ? block_ch8_1st->data + ch8_offset : zeros;
  // SPDIF OUT works now with ADAT
#if IMXRT_CACHE_ENABLED >= 2
  arm_dcache_flush_delete(toFlush, flushLen);
  //asm("dsb":::"memory"); // DO WE NEED THIS ?
#endif // IMXRT_CACHE_ENABLED >= 2  

  //Non-NZRI encoded 'empty' ADAT Frame
  /*
    (dest+0) = 0b10000100001000010000100001000010; // bit 0-31
    (dest+1) = 0b00010000100001000010000100001000; // bit 32-63
    (dest+2) = 0b01000010000100001000010000100001; // bit 64-95
    (dest+3) = 0b00001000010000100001000010000100; // bit 96-127

    (dest+4) = 0b00100001000010000100001000010000; // bit 128-159
    (dest+5) = 0b10000100001000010000100001000010; // bit 160-191
    (dest+6) = 0b00010000100001000010000100001000; // bit 192-223
    (dest+7) = 0b01000010000100001000000000010000; // bit 224-255

    dest+=8;

  */

  /*
    //NZRI encoded 'empty' ADAT Frame

    (dest+0) = 0b11111000001111100000111110000011; // bit 0-31
    (dest+1) = 0b11100000111110000011111000001111; // bit 32-63
    (dest+2) = 0b10000011111000001111100000111110; // bit 64-95
    (dest+3) = 0b00001111100000111110000011111000; // bit 96-127

    (dest+4) = 0b00111110000011111000001111100000; // bit 128-159
    (dest+5) = 0b11111000001111100000111110000011; // bit 160-191
    (dest+6) = 0b11100000111110000011111000001111; // bit 192-223
    (dest+7) = 0b10000011111000001111111111100000; // bit 224-255

    dest+=8;

  */

  do
  {
    /*
      // SPDIF OUT works now with ADAT
      #if IMXRT_CACHE_ENABLED >= 2
      arm_dcache_flush_delete(toFlush, flushLen);
      //asm("dsb":::"memory"); // DO WE NEED THIS ?
      #endif // IMXRT_CACHE_ENABLED >= 2
    */
    sample1 = (*src1++);
    sample2 = (*src2++);
    sample3 = (*src3++);
    sample4 = (*src4++);
    sample5 = (*src5++);
    sample6 = (*src6++);
    sample7 = (*src7++);
    sample8 = (*src8++);

    uint32_t value;
    uint32_t nzrivalue;

    value =         0b10000100001000010000100001000010 /* bit  0-31 */
                    //                b00000000000000001000000000000000 // start of 16 bit sample
                    | ((sample1 << 15) & 0b01111000000000000000000000000000)
                    | ((sample1 << 14) & 0b00000011110000000000000000000000)
                    | ((sample1 << 13) & 0b00000000000111100000000000000000)
                    | ((sample1 << 12) & 0b00000000000000001111000000000000)
                    | ((sample2 >> 15) & 0b00000000000000000000000000000001)
                    ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 0) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00010000100001000010000100001000 /* bit 32-63 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample2 << 17) & 0b11100000000000000000000000000000)
                   | ((sample2 << 16) & 0b00001111000000000000000000000000)
                   | ((sample2 << 15) & 0b00000000011110000000000000000000)
                   | ((sample2 << 14) & 0b00000000000000111100000000000000)
                   | ((sample3 >> 13) & 0b00000000000000000000000000000111)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 1) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;


    value =        0b01000010000100001000010000100001 /* bit 64-95 */
                   //                b00000000000000001000000000000000 // start of 16 bit sample
                   | ((sample3 << 19) & 0b10000000000000000000000000000000)
                   | ((sample3 << 18) & 0b00111100000000000000000000000000)
                   | ((sample3 << 17) & 0b00000001111000000000000000000000)
                   | ((sample3 << 16) & 0b00000000000011110000000000000000)
                   | ((sample4 >> 11) & 0b00000000000000000000000000011110)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 2) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00001000010000100001000010000100 /* bit 96-127 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample4 << 20) & 0b11110000000000000000000000000000)
                   | ((sample4 << 19) & 0b00000111100000000000000000000000)
                   | ((sample4 << 18) & 0b00000000001111000000000000000000)
                   | ((sample5 >> 9 ) & 0b00000000000000000000000001111000)
                   | ((sample5 >> 10) & 0b00000000000000000000000000000011)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 3) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00100001000010000100001000010000 /* bit 128-159 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample5 << 22) & 0b11000000000000000000000000000000)
                   | ((sample5 << 21) & 0b00011110000000000000000000000000)
                   | ((sample5 << 20) & 0b00000000111100000000000000000000)
                   | ((sample6 >> 7 ) & 0b00000000000000000000000111100000)
                   | ((sample6 >> 8 ) & 0b00000000000000000000000000001111)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 4) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b10000100001000010000100001000010 /* bit 160-191 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample6 << 23) & 0b01111000000000000000000000000000)
                   | ((sample6 << 22) & 0b00000011110000000000000000000000)
                   | ((sample7 >> 5 ) & 0b00000000000000000000011110000000)
                   | ((sample7 >> 6 ) & 0b00000000000000000000000000111100)
                   | ((sample7 >> 7 ) & 0b00000000000000000000000000000001)
                   ;

    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 5) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00010000100001000010000100001000 /* bit 192-223 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample7 << 25) & 0b11100000000000000000000000000000)
                   | ((sample7 << 24) & 0b00001111000000000000000000000000)
                   | ((sample8 >> 3 ) & 0b00000000000000000001111000000000)
                   | ((sample8 >> 4 ) & 0b00000000000000000000000011110000)
                   | ((sample8 >> 5 ) & 0b00000000000000000000000000000111)
                   ;

    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 6) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b01000010000100001000000000010000 /* bit 224-255 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample8 << 27) & 0b10000000000000000000000000000000)
                   | ((sample8 << 26) & 0b00111100000000000000000000000000)
                   ;

    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 7) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    dest += 8;
  } while (dest < end);
  /*
    #if IMXRT_CACHE_ENABLED >= 2
    arm_dcache_flush_delete(toFlush, flushLen);
    #endif // IMXRT_CACHE_ENABLED >= 2
  */

  /*
    block = AudioOutputADAT::block_ch1_1st;
    if (block) {
    offset = AudioOutputADAT::ch1_offset;
    src = &block->data[offset];
    do {

      sample = (uint32_t)*src++;

    } while (dest < end);
    offset += AUDIO_BLOCK_SAMPLES/2;
    if (offset < AUDIO_BLOCK_SAMPLES) {
      AudioOutputADAT::ch1_offset = offset;
    } else {
      AudioOutputADAT::ch1_offset = 0;
      AudioStream::release(block);
      AudioOutputADAT::block_ch1_1st = AudioOutputADAT::block_ch1_2nd;
      AudioOutputADAT::block_ch1_2nd = NULL;
    }
    } else {
    sample = 0;

    do {
      // *(dest+0) = 0b11111000001111100000111110000011; // bit 0-31
      // *(dest+1) = 0b11100000111110000011111000001111; // bit 32-63
      // *(dest+2) = 0b10000011111000001111100000111110; // bit 64-95
      // *(dest+3) = 0b00001111100000111110000011111000; // bit 96-127

      // *(dest+4) = 0b00111110000011111000001111100000; // bit 128-159
      // *(dest+5) = 0b11111000001111100000111110000011; // bit 160-191
      // *(dest+6) = 0b11100000111110000011111000001111; // bit 192-223
      // *(dest+7) = 0b10000011111000001111111111100000; // bit 224-255
      dest+=8;
    } while (dest < end);
    }


    dest -= AUDIO_BLOCK_SAMPLES * 8/2 - 8/2;
    block = AudioOutputADAT::block_ch2_1st;
    if (block) {
    offset = AudioOutputADAT::ch2_offset;
    src = &block->data[offset];

    do {
      sample = *src++;

      // *(dest+0) = 0b00111110000011111000001111100000; // bit 128-159
      // *(dest+1) = 0b11111000001111100000111110000011; // bit 160-191
      // *(dest+2) = 0b11100000111110000011111000001111; // bit 192-223
      // *(dest+3) = 0b10000011111000001111111111100000; // bit 224-255
      dest+=8;
    } while (dest < end);

    offset += AUDIO_BLOCK_SAMPLES/2;
    if (offset < AUDIO_BLOCK_SAMPLES) {
      AudioOutputADAT::ch2_offset = offset;
    } else {
      AudioOutputADAT::ch2_offset = 0;
      AudioStream::release(block);
      AudioOutputADAT::block_ch2_1st = AudioOutputADAT::block_ch2_2nd;
      AudioOutputADAT::block_ch2_2nd = NULL;
    }
    } else {
      // *(dest+0) = 0b00111110000011111000001111100000; // bit 128-159
      // *(dest+1) = 0b11111000001111100000111110000011; // bit 160-191
      // *(dest+2) = 0b11100000111110000011111000001111; // bit 192-223
      // *(dest+3) = 0b10000011111000001111111111100000; // bit 224-255
      dest+=8;
    }*/

  if (block_ch1_1st) {
    if (ch1_offset == 0) {
      ch1_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch1_offset = 0;
      release(block_ch1_1st);
      block_ch1_1st = block_ch1_2nd;
      block_ch1_2nd = NULL;
    }
  }
  if (block_ch2_1st) {
    if (ch2_offset == 0) {
      ch2_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch2_offset = 0;
      release(block_ch2_1st);
      block_ch2_1st = block_ch2_2nd;
      block_ch2_2nd = NULL;
    }
  }
  if (block_ch3_1st) {
    if (ch3_offset == 0) {
      ch3_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch3_offset = 0;
      release(block_ch3_1st);
      block_ch3_1st = block_ch3_2nd;
      block_ch3_2nd = NULL;
    }
  }
  if (block_ch4_1st) {
    if (ch4_offset == 0) {
      ch4_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch4_offset = 0;
      release(block_ch4_1st);
      block_ch4_1st = block_ch4_2nd;
      block_ch4_2nd = NULL;
    }
  }
  if (block_ch5_1st) {
    if (ch5_offset == 0) {
      ch5_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch5_offset = 0;
      release(block_ch5_1st);
      block_ch5_1st = block_ch5_2nd;
      block_ch5_2nd = NULL;
    }
  }
  if (block_ch6_1st) {
    if (ch6_offset == 0) {
      ch6_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch6_offset = 0;
      release(block_ch6_1st);
      block_ch6_1st = block_ch6_2nd;
      block_ch6_2nd = NULL;
    }
  }
  if (block_ch7_1st) {
    if (ch7_offset == 0) {
      ch7_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch7_offset = 0;
      release(block_ch7_1st);
      block_ch7_1st = block_ch7_2nd;
      block_ch7_2nd = NULL;
    }
  }
  if (block_ch8_1st) {
    if (ch8_offset == 0) {
      ch8_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch8_offset = 0;
      release(block_ch8_1st);
      block_ch8_1st = block_ch8_2nd;
      block_ch8_2nd = NULL;
    }
  }
  /*
    #if defined(__IMXRT1052__) || defined(__IMXRT1062__)
    // if you dont have this you just get noise! - AJ, https://forum.pjrc.com/threads/60914-ADAT-white-noise-on-Teensy-4-0
    arm_dcache_flush_delete((void *)((uint32_t)dest - sizeof(ADAT3_tx_buffer) / 2), sizeof(ADAT3_tx_buffer) / 2 );
    #endif
  */
}

void AudioOutputADAT3::mute_PCM(const bool mute)
{
  // The TMR causes the Transmit Data pin to be tri-stated for the length of each selected
  // word and the transmit FIFO is not read for masked words.
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
  // configure transmitter T 4.x
  // SAI Transmit Mask Register (TMR)
  I2S3_TMR = mute; // 1 mute - 0 unmute, I2S3_TMR
#endif
}

void AudioOutputADAT3::update(void)
{

  audio_block_t *block, *tmp;

  block = receiveReadOnly(0); // input 0 = channel 1
  if (block) {
    __disable_irq();
    if (block_ch1_1st == NULL) {
      block_ch1_1st = block;
      ch1_offset = 0;
      __enable_irq();
    } else if (block_ch1_2nd == NULL) {
      block_ch1_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch1_1st;
      block_ch1_1st = block_ch1_2nd;
      block_ch1_2nd = block;
      ch1_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(1); // input 1 = channel 2
  if (block) {
    __disable_irq();
    if (block_ch2_1st == NULL) {
      block_ch2_1st = block;
      ch2_offset = 0;
      __enable_irq();
    } else if (block_ch2_2nd == NULL) {
      block_ch2_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch2_1st;
      block_ch2_1st = block_ch2_2nd;
      block_ch2_2nd = block;
      ch2_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(2); // channel 3
  if (block) {
    __disable_irq();
    if (block_ch3_1st == NULL) {
      block_ch3_1st = block;
      ch3_offset = 0;
      __enable_irq();
    } else if (block_ch3_2nd == NULL) {
      block_ch3_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch3_1st;
      block_ch3_1st = block_ch3_2nd;
      block_ch3_2nd = block;
      ch3_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(3); // channel 4
  if (block) {
    __disable_irq();
    if (block_ch4_1st == NULL) {
      block_ch4_1st = block;
      ch4_offset = 0;
      __enable_irq();
    } else if (block_ch4_2nd == NULL) {
      block_ch4_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch4_1st;
      block_ch4_1st = block_ch4_2nd;
      block_ch4_2nd = block;
      ch4_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(4); // channel 5
  if (block) {
    __disable_irq();
    if (block_ch5_1st == NULL) {
      block_ch5_1st = block;
      ch5_offset = 0;
      __enable_irq();
    } else if (block_ch5_2nd == NULL) {
      block_ch5_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch5_1st;
      block_ch5_1st = block_ch5_2nd;
      block_ch5_2nd = block;
      ch5_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(5); // channel 6
  if (block) {
    __disable_irq();
    if (block_ch6_1st == NULL) {
      block_ch6_1st = block;
      ch6_offset = 0;
      __enable_irq();
    } else if (block_ch6_2nd == NULL) {
      block_ch6_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch6_1st;
      block_ch6_1st = block_ch6_2nd;
      block_ch6_2nd = block;
      ch6_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(6); // channel 7
  if (block) {
    __disable_irq();
    if (block_ch7_1st == NULL) {
      block_ch7_1st = block;
      ch7_offset = 0;
      __enable_irq();
    } else if (block_ch7_2nd == NULL) {
      block_ch7_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch7_1st;
      block_ch7_1st = block_ch7_2nd;
      block_ch7_2nd = block;
      ch7_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(7); // channel 8
  if (block) {
    __disable_irq();
    if (block_ch8_1st == NULL) {
      block_ch8_1st = block;
      ch8_offset = 0;
      __enable_irq();
    } else if (block_ch8_2nd == NULL) {
      block_ch8_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch8_1st;
      block_ch8_1st = block_ch8_2nd;
      block_ch8_2nd = block;
      ch8_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
}

// Teensy 4.x simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT3::set_Freq(int _FREQ)
{
  // TODO: should we set & clear the I2S_TCSR_SR bit here?
  setI2SFreq(_FREQ * 2); // for ADAT we need (MCLK @24,576,000Mhz 48000kHz) or (MCLK @22,579,200Mhz 44100kHz)
}

void AudioOutputADAT3::config_ADAT(void)
{
  // Set this to double the audio samplerate for adat to work! - AJ
  int fs = AUDIO_SAMPLE_RATE_EXACT * 2; // (default)

  //CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON); // SAI1
  CCM_CCGR5 |= CCM_CCGR5_SAI3(CCM_CCGR_ON); // SAI3

  // if either transmitter or receiver is enabled, do nothing /AAA from output_i2s
  if (I2S3_TCSR & I2S_TCSR_TE) return;
  if (I2S3_RCSR & I2S_RCSR_RE) return; // TODO-<<<<<SEE LINE 128

  AudioOutputADAT3::setI2SFreq(fs); // set PLL, CCM_ANALOG

  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);

  // clear SAI3_CLK register locations
  CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI3_CLK_SEL_MASK))
#if ENABLE_PLL5_VIDEO
               | CCM_CSCMR1_SAI3_CLK_SEL(1); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4     PLL5 VIDEO TEST
#else
               | CCM_CSCMR1_SAI3_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
#endif  // ENABLE_PLL5_VIDEO

  // PRED, PODF
  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI3_CLK_PRED_MASK | CCM_CS1CDR_SAI3_CLK_PODF_MASK))
               | CCM_CS1CDR_SAI3_CLK_PRED(n1 / 2 - 1) // &0x07
               | CCM_CS1CDR_SAI3_CLK_PODF(n2 - 1); // &0x3f
  // Select SAI3_MCLK3
  IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI3_MCLK3_SEL_MASK)) // SAI3 MCLK3 PIN30, EMC37
                    | (IOMUXC_GPR_GPR1_SAI3_MCLK_DIR | IOMUXC_GPR_GPR1_SAI3_MCLK3_SEL(0)); //SAI3 MCLK3 source select 00 ccm.spdif0_clk_root
  // SAI3 MCLK3 source select
  // 00 ccm.spdif0_clk_root
  // 01 iomux.spdif_tx_clk2
  // 10 spdif.spdif_srclk
  // 11 spdif.spdif_outclock

  // int rsync = 1; // 1b - Synchronous with receiver
  int tsync = 0; // transmitter -- 0b - Asynchronous mode, 1b - Synchronous with receiver.

  // configure transmitter
  I2S3_TMR = 0; // SAI Transmit Mask Register (TMR)
  I2S3_TCR1 = I2S_TCR1_RFW(4);  //now 4 was 0, supposed to be: I2S_TCR1_TFW(n), Transmit FIFO Watermark
  I2S3_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  I2S3_TCR3 = I2S_TCR3_TCE; // Enable
  //  MCLK Select
  // 00b - Bus Clock selected.
  // 01b - Master Clock (MCLK) 1 option selected.
  // 10b - Master Clock (MCLK) 2 option selected.
  // 11b - Master Clock (MCLK) 3 option selected.

  // i want.. 8 Words per Frame 32 Bit Word-Length -> 256 Bit Frame-Length, MSB First:
  I2S3_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
  I2S3_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

  // configure receiver, ADAT IN not implemented yet.
  I2S3_RCSR = 0; // disabled
  /*
     I2S3_RMR = 0;
     I2S3_RCSR = (1<<25); //Reset
     I2S3_RCR1 = I2S_RCR1_RFW(0);

     // should this be I2S_TCR4_FRSZ(7) as above? both 3 and 7 work in both places - AJ
     I2S3_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
     I2S3_RCR3 = I2S_RCR3_RCE;

     I2S3_RCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
     I2S3_RCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
  */

  // TEST
  CORE_PIN30_CONFIG = 3;  //1:MCLK3-SAI3,  22,579,200Mhz--@44100kHz, 24,576,000Mhz--@48000kHz

#if 0
  //debug only:
  CORE_PIN30_CONFIG = 3;  //1:MCLK3-SAI3,  22,579,200Mhz--@44100kHz, 24,576,000Mhz--@48000kHz
  //  TODO SEE IF PIN AVAL.  //1:RX_BCLK 5.6 MHz
  //  TODO SEE IF PIN AVAL.  //1:RX_SYNC 44.1 KHz
  CORE_PIN31_CONFIG  = 3;  //1:TX_DATA0-SAI3
  //  TODO SEE IF PIN AVAL.  //1:RX_DATA0
#endif
}

void AudioOutputADAT3::setI2SFreq(int freq) {  // PLL4, CCM_ANALOG
  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (freq * 256 * n1);
  double C = ((double)freq * 256 * n1 * n2) / 24000000;
  int c0 = C;
  int c2 = 10000;
  int c1 = C * c2 - (c0 * c2);

#if ENABLE_PLL5_VIDEO  // - Set PLL5 CCM_ANALOG_PLL_VIDEO
  //if (!force && (CCM_ANALOG_PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_ENABLE)) return;

  CCM_ANALOG_PLL_VIDEO = CCM_ANALOG_PLL_VIDEO_BYPASS | CCM_ANALOG_PLL_VIDEO_ENABLE
                         | CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
                         | CCM_ANALOG_PLL_VIDEO_DIV_SELECT(c0);

  CCM_ANALOG_PLL_VIDEO_NUM   = c1 & CCM_ANALOG_PLL_VIDEO_NUM_MASK;
  CCM_ANALOG_PLL_VIDEO_DENOM = c2 & CCM_ANALOG_PLL_VIDEO_DENOM_MASK;

  CCM_ANALOG_PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_POWERDOWN;//Switch on PLL
  while (!(CCM_ANALOG_PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_LOCK)) {}; // Wait for pll-lock

  // -- PLL5 b10, Dec2, -- divide by 1
  // -- PLL5 b01, Dec1, -- divide by 2
  CCM_ANALOG_MISC2 = CCM_ANALOG_MISC2_VIDEO_DIV(1); // divide by 2

  CCM_ANALOG_PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_BYPASS;// Disable Bypass
  // CCM SAI3
  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI3_CLK_PRED_MASK | CCM_CS1CDR_SAI3_CLK_PODF_MASK))
               | CCM_CS1CDR_SAI3_CLK_PRED(n1 / 2 - 1) // &0x07
               | CCM_CS1CDR_SAI3_CLK_PODF(n2 - 1); // &0x3f
#else
  // set PLL4 CCM_ANALOG_PLL_AUDIO
  set_audioClock(c0, c1, c2, true); // see \libraries\Audio\utility\imxrt_hw.ccp
  // CCM SAI3
  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI3_CLK_PRED_MASK | CCM_CS1CDR_SAI3_CLK_PODF_MASK))
               | CCM_CS1CDR_SAI3_CLK_PRED(n1 / 2 - 1) // &0x07
               | CCM_CS1CDR_SAI3_CLK_PODF(n2 - 1); // &0x3f
#endif  // END ENABLE_PLLn

#if 0
  //debug only:
  Serial.printf("SetI2SFreq(%d)\n", freq);
  //Serial.print("Freq "),Serial.println(freq);
  Serial.print("c0 DIV   "), Serial.println(c0);
  Serial.print("c1 NUM   "), Serial.println(c1);
  Serial.print("c2 DENOM "), Serial.println(c2);
  Serial.print("n1 PRED/ "), Serial.println(n1);
  Serial.print("n2 PODF/ "), Serial.println(n2);
  Serial.println();

  // -- Freq 88200    for 44100kHz ADAT, MCLK1 22,579,125Mhz -- we need 22,579,200MHz
  // c0 DIV   30      -- OSC_24000000Mhz *(30 + (1055/10000)) = 722,532,000MHz
  // c1 NUM   1055    <--- issue
  // c2 DENOM 10000
  // n1 PRED/ 4       -- 722,532,000 / 4 = 180,633,000MHz
  // n2 PODF/ 8       -- 180,633,000 / 8 = 22,579,125
  // MCLK--22,579,125MHz

  // -- Freq 88200   for 44100kHz ADAT, MCLK1 22,579,200Mhz
  // c0 DIV   30     -- OSC_24000000Mhz *(30 + (1056/10000)) = 722,534,400MHz
  // c1 NUM   1056   <--- PLL frequency correction @88200
  // c2 DENOM 10000
  // n1 PRED/ 4      -- 722,534,400 / 4 = 180,633,600MHz
  // n2 PODF/ 8      -- 180,633,600 / 8 = 22,579,200
  // MCLK--22,579,200MHz -- good @88200

  // -- Freq 96000   for 48000kHz ADAT, MCLK1 24,576,000Mhz
  // c0 DIV   28     -- OSC_24000000Mhz *(28 + (6720/10000)) = 688,128,000
  // c1 NUM   6720
  // c2 DENOM 10000
  // n1 PRED/ 4      -- 688,128,000 / 4 = 172,032,000
  // n2 PODF/ 7      -- 172,032,000 / 7 = 24,576,000
  // MCLK-- 24,576,000MHz   good@48000

  // -- Freq 48000    for 48000kHz, MCLK1 12,288,000Mhz
  // c0 DIV   28      -- OSC_24000000Mhz *(28 + (6720/10000)) = 688,128,000MHz
  // c1 NUM   6720
  // c2 DENOM 10000
  // n1 PRED/ 4       -- 688,128,000 / 4 = 172,032,000MHz
  // n2 PODF/ 14      -- 172,032,000 / 14 = 12,288,000
  // MCLK--12,288,000MHz -- good @48000

  // -- Freq 44100    -- OSC_24000000Mhz *(28 + (2240/10000)) = 677,376,000
  // c0 DIV   28
  // c1 NUM   2240
  // c2 DENOM 10000
  // n1 PRED/ 4       -- 677,376,000 / 4  = 169,344,000
  // n2 PODF/ 15      -- 169,344,000 / 15 = 11,289,600MHz
  // MCLK--11,289,600MHz -- good @44100
#endif
}

#elif defined(KINETISL)
// Fix compile error on Teensy LC

void AudioOutputADAT3::mute_PCM(const bool mute)
{
  // Teensy LC return
}

// simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT3::set_Freq(int _FREQ)
{
  // Teensy LC return
}

void AudioOutputADAT3::begin(void)
{
  // Teensy LC return
}

void AudioOutputADAT3::update(void)
{

  audio_block_t *block;
  block = receiveReadOnly(0); // input 0 = ch1 channel
  if (block) release(block);
  block = receiveReadOnly(1); // input 1 = ch2 channel
  if (block) release(block);
  block = receiveReadOnly(2); // input 2 = ch3 channel
  if (block) release(block);
  block = receiveReadOnly(3); // input 3 = ch4 channel
  if (block) release(block);
  block = receiveReadOnly(4); // input 4 = ch5 channel
  if (block) release(block);
  block = receiveReadOnly(5); // input 5 = ch6 channel
  if (block) release(block);
  block = receiveReadOnly(6); // input 6 = ch7 channel
  if (block) release(block);
  block = receiveReadOnly(7); // input 7 = ch8 channel
  if (block) release(block);
}
#endif

#if defined(__MK66FX1M0__) || defined(__MK64FX512__) || defined(__MK20DX256__)
// empty code to allow compile (but no sound output) on other Teensy models
void AudioOutputADAT3::mute_PCM(const bool mute) {}
void AudioOutputADAT3::set_Freq(int _FREQ) {}
void AudioOutputADAT3::begin(void) {}
void AudioOutputADAT3::update(void) {}
#endif

ADAT SAI3 + PLL5_VIDEO
output_adat3.h
Code:
/* ADAT for Teensy 3.X
 * Copyright (c) 2017, Ernstjan Freriks,
 * Thanks to Frank Bösing & KPC & Paul Stoffregen!
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef output_ADAT3_h_
#define output_ADAT3_h_

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

class AudioOutputADAT3 : public AudioStream
{
public:
	AudioOutputADAT3(void) : AudioStream(8, inputQueueArray) { begin(); }
	virtual void update(void);
	void begin(void);
	static void mute_PCM(const bool mute);
	void set_Freq(int freq); // change the samplerate of ADAT at runtime, 44100 or 48000 only!!!
protected:
//  AudioOutputADAT3
	AudioOutputADAT3(int dummy): AudioStream(8, inputQueueArray) {}
	static void config_ADAT(void);
	static audio_block_t *block_ch1_1st;
	static audio_block_t *block_ch2_1st;
	static audio_block_t *block_ch3_1st;
	static audio_block_t *block_ch4_1st;
	static audio_block_t *block_ch5_1st;
	static audio_block_t *block_ch6_1st;
	static audio_block_t *block_ch7_1st;
	static audio_block_t *block_ch8_1st;
	static bool update_responsibility;
	static DMAChannel dma;
	static void isr(void);
	static void setI2SFreq(int freq);
private:
	//static uint32_t vucp;
	static audio_block_t *block_ch1_2nd;
	static audio_block_t *block_ch2_2nd;
	static audio_block_t *block_ch3_2nd;
	static audio_block_t *block_ch4_2nd;
	static audio_block_t *block_ch5_2nd;
	static audio_block_t *block_ch6_2nd;
	static audio_block_t *block_ch7_2nd;
	static audio_block_t *block_ch8_2nd;
	static uint16_t ch1_offset;
	static uint16_t ch2_offset;
	static uint16_t ch3_offset;
	static uint16_t ch4_offset;
	static uint16_t ch5_offset;
	static uint16_t ch6_offset;
	static uint16_t ch7_offset;
	static uint16_t ch8_offset;
	audio_block_t *inputQueueArray[8];
};

#endif

Updated ADAT1 SAI1 + PLL5 VIDEO
output_adat.cpp
Code:
/* ADAT for Teensy 4.X
   Copyright (c) 2017, Ernstjan Freriks,
   Thanks to Frank Bösing & KPC & Paul Stoffregen!

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to deal
   in the Software without restriction, including without limitation the rights
   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice, development funding notice, and this permission
   notice shall be included in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   THE SOFTWARE.
*/

// Chris O. 4/03/2023
// SAI1 ADAT + PLL5 VIDEO -- Teensy 4.x only !!!!
// output_adat.cpp
// T4.x -Set Transmit FIFO Watermark 4, now was 0.
// fix @ LINE 218
//  SPDIF OUT works now with ADAT
//  #if IMXRT_CACHE_ENABLED >= 2
//  arm_dcache_flush_delete(toFlush, flushLen);

#if defined(__IMXRT1062__) // Teensy 4.x only !!!!
/* Use PLL5_VIDEO - must be 1 */
#define ENABLE_PLL5_VIDEO 1

#if ENABLE_PLL5_VIDEO
#pragma message("Use PLL5_VIDEO - ADAT SAI1")
// P.1114, not in imxrt.h
#define CCM_ANALOG_PLL_VIDEO_NUM_MASK		(((1<<29)-1))
#define CCM_ANALOG_PLL_VIDEO_DENOM_MASK		(((1<<29)-1))
#else
#pragma message("Use PLL4_AUDIO")
#endif  // ENABLE_PLL5_VIDEO

#endif

#include <Arduino.h>
#include "output_adat.h"
#include "utility/imxrt_hw.h"

#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__)

audio_block_t * AudioOutputADAT::block_ch1_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch2_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch3_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch4_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch5_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch6_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch7_1st = NULL;
audio_block_t * AudioOutputADAT::block_ch8_1st = NULL;

audio_block_t * AudioOutputADAT::block_ch1_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch2_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch3_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch4_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch5_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch6_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch7_2nd = NULL;
audio_block_t * AudioOutputADAT::block_ch8_2nd = NULL;

uint16_t  AudioOutputADAT::ch1_offset = 0;
uint16_t  AudioOutputADAT::ch2_offset = 0;
uint16_t  AudioOutputADAT::ch3_offset = 0;
uint16_t  AudioOutputADAT::ch4_offset = 0;
uint16_t  AudioOutputADAT::ch5_offset = 0;
uint16_t  AudioOutputADAT::ch6_offset = 0;
uint16_t  AudioOutputADAT::ch7_offset = 0;
uint16_t  AudioOutputADAT::ch8_offset = 0;

bool AudioOutputADAT::update_responsibility = false;

DMAMEM __attribute__((aligned(32))) static uint32_t ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8]; //4 KB, AUDIO_BLOCK_SAMPLES is usually 128
DMAChannel AudioOutputADAT::dma(false);

static const uint32_t zerodata[AUDIO_BLOCK_SAMPLES / 4] = {0};

// These are the lookup tables. There are four of them so that the remainder of the 32 bit result can easily be XORred with the next 8 bits.
static const uint32_t LookupTable_firstword[256]  = { 0x0, 0x1ffffff, 0x3ffffff, 0x2000000, 0x7ffffff, 0x6000000, 0x4000000, 0x5ffffff, 0xfffffff, 0xe000000, 0xc000000, 0xdffffff, 0x8000000, 0x9ffffff, 0xbffffff, 0xa000000, 0x1fffffff, 0x1e000000, 0x1c000000, 0x1dffffff, 0x18000000, 0x19ffffff, 0x1bffffff, 0x1a000000, 0x10000000, 0x11ffffff, 0x13ffffff, 0x12000000, 0x17ffffff, 0x16000000, 0x14000000, 0x15ffffff, 0x3fffffff, 0x3e000000, 0x3c000000, 0x3dffffff, 0x38000000, 0x39ffffff, 0x3bffffff, 0x3a000000, 0x30000000, 0x31ffffff, 0x33ffffff, 0x32000000, 0x37ffffff, 0x36000000, 0x34000000, 0x35ffffff, 0x20000000, 0x21ffffff, 0x23ffffff, 0x22000000, 0x27ffffff, 0x26000000, 0x24000000, 0x25ffffff, 0x2fffffff, 0x2e000000, 0x2c000000, 0x2dffffff, 0x28000000, 0x29ffffff, 0x2bffffff, 0x2a000000, 0x7fffffff, 0x7e000000, 0x7c000000, 0x7dffffff, 0x78000000, 0x79ffffff, 0x7bffffff, 0x7a000000, 0x70000000, 0x71ffffff, 0x73ffffff, 0x72000000, 0x77ffffff, 0x76000000, 0x74000000, 0x75ffffff, 0x60000000, 0x61ffffff, 0x63ffffff, 0x62000000, 0x67ffffff, 0x66000000, 0x64000000, 0x65ffffff, 0x6fffffff, 0x6e000000, 0x6c000000, 0x6dffffff, 0x68000000, 0x69ffffff, 0x6bffffff, 0x6a000000, 0x40000000, 0x41ffffff, 0x43ffffff, 0x42000000, 0x47ffffff, 0x46000000, 0x44000000, 0x45ffffff, 0x4fffffff, 0x4e000000, 0x4c000000, 0x4dffffff, 0x48000000, 0x49ffffff, 0x4bffffff, 0x4a000000, 0x5fffffff, 0x5e000000, 0x5c000000, 0x5dffffff, 0x58000000, 0x59ffffff, 0x5bffffff, 0x5a000000, 0x50000000, 0x51ffffff, 0x53ffffff, 0x52000000, 0x57ffffff, 0x56000000, 0x54000000, 0x55ffffff, 0xffffffff, 0xfe000000, 0xfc000000, 0xfdffffff, 0xf8000000, 0xf9ffffff, 0xfbffffff, 0xfa000000, 0xf0000000, 0xf1ffffff, 0xf3ffffff, 0xf2000000, 0xf7ffffff, 0xf6000000, 0xf4000000, 0xf5ffffff, 0xe0000000, 0xe1ffffff, 0xe3ffffff, 0xe2000000, 0xe7ffffff, 0xe6000000, 0xe4000000, 0xe5ffffff, 0xefffffff, 0xee000000, 0xec000000, 0xedffffff, 0xe8000000, 0xe9ffffff, 0xebffffff, 0xea000000, 0xc0000000, 0xc1ffffff, 0xc3ffffff, 0xc2000000, 0xc7ffffff, 0xc6000000, 0xc4000000, 0xc5ffffff, 0xcfffffff, 0xce000000, 0xcc000000, 0xcdffffff, 0xc8000000, 0xc9ffffff, 0xcbffffff, 0xca000000, 0xdfffffff, 0xde000000, 0xdc000000, 0xddffffff, 0xd8000000, 0xd9ffffff, 0xdbffffff, 0xda000000, 0xd0000000, 0xd1ffffff, 0xd3ffffff, 0xd2000000, 0xd7ffffff, 0xd6000000, 0xd4000000, 0xd5ffffff, 0x80000000, 0x81ffffff, 0x83ffffff, 0x82000000, 0x87ffffff, 0x86000000, 0x84000000, 0x85ffffff, 0x8fffffff, 0x8e000000, 0x8c000000, 0x8dffffff, 0x88000000, 0x89ffffff, 0x8bffffff, 0x8a000000, 0x9fffffff, 0x9e000000, 0x9c000000, 0x9dffffff, 0x98000000, 0x99ffffff, 0x9bffffff, 0x9a000000, 0x90000000, 0x91ffffff, 0x93ffffff, 0x92000000, 0x97ffffff, 0x96000000, 0x94000000, 0x95ffffff, 0xbfffffff, 0xbe000000, 0xbc000000, 0xbdffffff, 0xb8000000, 0xb9ffffff, 0xbbffffff, 0xba000000, 0xb0000000, 0xb1ffffff, 0xb3ffffff, 0xb2000000, 0xb7ffffff, 0xb6000000, 0xb4000000, 0xb5ffffff, 0xa0000000, 0xa1ffffff, 0xa3ffffff, 0xa2000000, 0xa7ffffff, 0xa6000000, 0xa4000000, 0xa5ffffff, 0xafffffff, 0xae000000, 0xac000000, 0xadffffff, 0xa8000000, 0xa9ffffff, 0xabffffff, 0xaa000000};
static const uint32_t LookupTable_secondword[256] = { 0x0, 0x1ffff, 0x3ffff, 0x20000, 0x7ffff, 0x60000, 0x40000, 0x5ffff, 0xfffff, 0xe0000, 0xc0000, 0xdffff, 0x80000, 0x9ffff, 0xbffff, 0xa0000, 0x1fffff, 0x1e0000, 0x1c0000, 0x1dffff, 0x180000, 0x19ffff, 0x1bffff, 0x1a0000, 0x100000, 0x11ffff, 0x13ffff, 0x120000, 0x17ffff, 0x160000, 0x140000, 0x15ffff, 0x3fffff, 0x3e0000, 0x3c0000, 0x3dffff, 0x380000, 0x39ffff, 0x3bffff, 0x3a0000, 0x300000, 0x31ffff, 0x33ffff, 0x320000, 0x37ffff, 0x360000, 0x340000, 0x35ffff, 0x200000, 0x21ffff, 0x23ffff, 0x220000, 0x27ffff, 0x260000, 0x240000, 0x25ffff, 0x2fffff, 0x2e0000, 0x2c0000, 0x2dffff, 0x280000, 0x29ffff, 0x2bffff, 0x2a0000, 0x7fffff, 0x7e0000, 0x7c0000, 0x7dffff, 0x780000, 0x79ffff, 0x7bffff, 0x7a0000, 0x700000, 0x71ffff, 0x73ffff, 0x720000, 0x77ffff, 0x760000, 0x740000, 0x75ffff, 0x600000, 0x61ffff, 0x63ffff, 0x620000, 0x67ffff, 0x660000, 0x640000, 0x65ffff, 0x6fffff, 0x6e0000, 0x6c0000, 0x6dffff, 0x680000, 0x69ffff, 0x6bffff, 0x6a0000, 0x400000, 0x41ffff, 0x43ffff, 0x420000, 0x47ffff, 0x460000, 0x440000, 0x45ffff, 0x4fffff, 0x4e0000, 0x4c0000, 0x4dffff, 0x480000, 0x49ffff, 0x4bffff, 0x4a0000, 0x5fffff, 0x5e0000, 0x5c0000, 0x5dffff, 0x580000, 0x59ffff, 0x5bffff, 0x5a0000, 0x500000, 0x51ffff, 0x53ffff, 0x520000, 0x57ffff, 0x560000, 0x540000, 0x55ffff, 0xffffff, 0xfe0000, 0xfc0000, 0xfdffff, 0xf80000, 0xf9ffff, 0xfbffff, 0xfa0000, 0xf00000, 0xf1ffff, 0xf3ffff, 0xf20000, 0xf7ffff, 0xf60000, 0xf40000, 0xf5ffff, 0xe00000, 0xe1ffff, 0xe3ffff, 0xe20000, 0xe7ffff, 0xe60000, 0xe40000, 0xe5ffff, 0xefffff, 0xee0000, 0xec0000, 0xedffff, 0xe80000, 0xe9ffff, 0xebffff, 0xea0000, 0xc00000, 0xc1ffff, 0xc3ffff, 0xc20000, 0xc7ffff, 0xc60000, 0xc40000, 0xc5ffff, 0xcfffff, 0xce0000, 0xcc0000, 0xcdffff, 0xc80000, 0xc9ffff, 0xcbffff, 0xca0000, 0xdfffff, 0xde0000, 0xdc0000, 0xddffff, 0xd80000, 0xd9ffff, 0xdbffff, 0xda0000, 0xd00000, 0xd1ffff, 0xd3ffff, 0xd20000, 0xd7ffff, 0xd60000, 0xd40000, 0xd5ffff, 0x800000, 0x81ffff, 0x83ffff, 0x820000, 0x87ffff, 0x860000, 0x840000, 0x85ffff, 0x8fffff, 0x8e0000, 0x8c0000, 0x8dffff, 0x880000, 0x89ffff, 0x8bffff, 0x8a0000, 0x9fffff, 0x9e0000, 0x9c0000, 0x9dffff, 0x980000, 0x99ffff, 0x9bffff, 0x9a0000, 0x900000, 0x91ffff, 0x93ffff, 0x920000, 0x97ffff, 0x960000, 0x940000, 0x95ffff, 0xbfffff, 0xbe0000, 0xbc0000, 0xbdffff, 0xb80000, 0xb9ffff, 0xbbffff, 0xba0000, 0xb00000, 0xb1ffff, 0xb3ffff, 0xb20000, 0xb7ffff, 0xb60000, 0xb40000, 0xb5ffff, 0xa00000, 0xa1ffff, 0xa3ffff, 0xa20000, 0xa7ffff, 0xa60000, 0xa40000, 0xa5ffff, 0xafffff, 0xae0000, 0xac0000, 0xadffff, 0xa80000, 0xa9ffff, 0xabffff, 0xaa0000};
static const uint32_t LookupTable_thirdword[256]  = { 0x0, 0x1ff, 0x3ff, 0x200, 0x7ff, 0x600, 0x400, 0x5ff, 0xfff, 0xe00, 0xc00, 0xdff, 0x800, 0x9ff, 0xbff, 0xa00, 0x1fff, 0x1e00, 0x1c00, 0x1dff, 0x1800, 0x19ff, 0x1bff, 0x1a00, 0x1000, 0x11ff, 0x13ff, 0x1200, 0x17ff, 0x1600, 0x1400, 0x15ff, 0x3fff, 0x3e00, 0x3c00, 0x3dff, 0x3800, 0x39ff, 0x3bff, 0x3a00, 0x3000, 0x31ff, 0x33ff, 0x3200, 0x37ff, 0x3600, 0x3400, 0x35ff, 0x2000, 0x21ff, 0x23ff, 0x2200, 0x27ff, 0x2600, 0x2400, 0x25ff, 0x2fff, 0x2e00, 0x2c00, 0x2dff, 0x2800, 0x29ff, 0x2bff, 0x2a00, 0x7fff, 0x7e00, 0x7c00, 0x7dff, 0x7800, 0x79ff, 0x7bff, 0x7a00, 0x7000, 0x71ff, 0x73ff, 0x7200, 0x77ff, 0x7600, 0x7400, 0x75ff, 0x6000, 0x61ff, 0x63ff, 0x6200, 0x67ff, 0x6600, 0x6400, 0x65ff, 0x6fff, 0x6e00, 0x6c00, 0x6dff, 0x6800, 0x69ff, 0x6bff, 0x6a00, 0x4000, 0x41ff, 0x43ff, 0x4200, 0x47ff, 0x4600, 0x4400, 0x45ff, 0x4fff, 0x4e00, 0x4c00, 0x4dff, 0x4800, 0x49ff, 0x4bff, 0x4a00, 0x5fff, 0x5e00, 0x5c00, 0x5dff, 0x5800, 0x59ff, 0x5bff, 0x5a00, 0x5000, 0x51ff, 0x53ff, 0x5200, 0x57ff, 0x5600, 0x5400, 0x55ff, 0xffff, 0xfe00, 0xfc00, 0xfdff, 0xf800, 0xf9ff, 0xfbff, 0xfa00, 0xf000, 0xf1ff, 0xf3ff, 0xf200, 0xf7ff, 0xf600, 0xf400, 0xf5ff, 0xe000, 0xe1ff, 0xe3ff, 0xe200, 0xe7ff, 0xe600, 0xe400, 0xe5ff, 0xefff, 0xee00, 0xec00, 0xedff, 0xe800, 0xe9ff, 0xebff, 0xea00, 0xc000, 0xc1ff, 0xc3ff, 0xc200, 0xc7ff, 0xc600, 0xc400, 0xc5ff, 0xcfff, 0xce00, 0xcc00, 0xcdff, 0xc800, 0xc9ff, 0xcbff, 0xca00, 0xdfff, 0xde00, 0xdc00, 0xddff, 0xd800, 0xd9ff, 0xdbff, 0xda00, 0xd000, 0xd1ff, 0xd3ff, 0xd200, 0xd7ff, 0xd600, 0xd400, 0xd5ff, 0x8000, 0x81ff, 0x83ff, 0x8200, 0x87ff, 0x8600, 0x8400, 0x85ff, 0x8fff, 0x8e00, 0x8c00, 0x8dff, 0x8800, 0x89ff, 0x8bff, 0x8a00, 0x9fff, 0x9e00, 0x9c00, 0x9dff, 0x9800, 0x99ff, 0x9bff, 0x9a00, 0x9000, 0x91ff, 0x93ff, 0x9200, 0x97ff, 0x9600, 0x9400, 0x95ff, 0xbfff, 0xbe00, 0xbc00, 0xbdff, 0xb800, 0xb9ff, 0xbbff, 0xba00, 0xb000, 0xb1ff, 0xb3ff, 0xb200, 0xb7ff, 0xb600, 0xb400, 0xb5ff, 0xa000, 0xa1ff, 0xa3ff, 0xa200, 0xa7ff, 0xa600, 0xa400, 0xa5ff, 0xafff, 0xae00, 0xac00, 0xadff, 0xa800, 0xa9ff, 0xabff, 0xaa00};
static const uint32_t LookupTable_fourthword[256] = { 0x0, 0x1, 0x3, 0x2, 0x7, 0x6, 0x4, 0x5, 0xf, 0xe, 0xc, 0xd, 0x8, 0x9, 0xb, 0xa, 0x1f, 0x1e, 0x1c, 0x1d, 0x18, 0x19, 0x1b, 0x1a, 0x10, 0x11, 0x13, 0x12, 0x17, 0x16, 0x14, 0x15, 0x3f, 0x3e, 0x3c, 0x3d, 0x38, 0x39, 0x3b, 0x3a, 0x30, 0x31, 0x33, 0x32, 0x37, 0x36, 0x34, 0x35, 0x20, 0x21, 0x23, 0x22, 0x27, 0x26, 0x24, 0x25, 0x2f, 0x2e, 0x2c, 0x2d, 0x28, 0x29, 0x2b, 0x2a, 0x7f, 0x7e, 0x7c, 0x7d, 0x78, 0x79, 0x7b, 0x7a, 0x70, 0x71, 0x73, 0x72, 0x77, 0x76, 0x74, 0x75, 0x60, 0x61, 0x63, 0x62, 0x67, 0x66, 0x64, 0x65, 0x6f, 0x6e, 0x6c, 0x6d, 0x68, 0x69, 0x6b, 0x6a, 0x40, 0x41, 0x43, 0x42, 0x47, 0x46, 0x44, 0x45, 0x4f, 0x4e, 0x4c, 0x4d, 0x48, 0x49, 0x4b, 0x4a, 0x5f, 0x5e, 0x5c, 0x5d, 0x58, 0x59, 0x5b, 0x5a, 0x50, 0x51, 0x53, 0x52, 0x57, 0x56, 0x54, 0x55, 0xff, 0xfe, 0xfc, 0xfd, 0xf8, 0xf9, 0xfb, 0xfa, 0xf0, 0xf1, 0xf3, 0xf2, 0xf7, 0xf6, 0xf4, 0xf5, 0xe0, 0xe1, 0xe3, 0xe2, 0xe7, 0xe6, 0xe4, 0xe5, 0xef, 0xee, 0xec, 0xed, 0xe8, 0xe9, 0xeb, 0xea, 0xc0, 0xc1, 0xc3, 0xc2, 0xc7, 0xc6, 0xc4, 0xc5, 0xcf, 0xce, 0xcc, 0xcd, 0xc8, 0xc9, 0xcb, 0xca, 0xdf, 0xde, 0xdc, 0xdd, 0xd8, 0xd9, 0xdb, 0xda, 0xd0, 0xd1, 0xd3, 0xd2, 0xd7, 0xd6, 0xd4, 0xd5, 0x80, 0x81, 0x83, 0x82, 0x87, 0x86, 0x84, 0x85, 0x8f, 0x8e, 0x8c, 0x8d, 0x88, 0x89, 0x8b, 0x8a, 0x9f, 0x9e, 0x9c, 0x9d, 0x98, 0x99, 0x9b, 0x9a, 0x90, 0x91, 0x93, 0x92, 0x97, 0x96, 0x94, 0x95, 0xbf, 0xbe, 0xbc, 0xbd, 0xb8, 0xb9, 0xbb, 0xba, 0xb0, 0xb1, 0xb3, 0xb2, 0xb7, 0xb6, 0xb4, 0xb5, 0xa0, 0xa1, 0xa3, 0xa2, 0xa7, 0xa6, 0xa4, 0xa5, 0xaf, 0xae, 0xac, 0xad, 0xa8, 0xa9, 0xab, 0xaa};

void AudioOutputADAT::begin(void)
{

  dma.begin(true); // Allocate the DMA channel first

  block_ch1_1st = NULL;
  block_ch2_1st = NULL;
  block_ch3_1st = NULL;
  block_ch4_1st = NULL;
  block_ch5_1st = NULL;
  block_ch6_1st = NULL;
  block_ch7_1st = NULL;
  block_ch8_1st = NULL;
  // TODO: should we set & clear the I2S_TCSR_SR bit here?
  config_ADAT();
#if defined(KINETISK)
  CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0

  const int nbytes_mlno = 2 * 8; // 16 Bytes per minor loop

  dma.TCD->SADDR = ADAT_tx_buffer;
  dma.TCD->SOFF = 4;
  dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); //transfersize 2 = 32 bit, 5 = 32 byte
  dma.TCD->NBYTES_MLNO = nbytes_mlno;
  dma.TCD->SLAST = -sizeof(ADAT_tx_buffer);
  dma.TCD->DADDR = &I2S0_TDR0;
  dma.TCD->DOFF = 0;
  dma.TCD->CITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
  dma.TCD->DLASTSGA = 0;
  dma.TCD->BITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
  dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
  update_responsibility = update_setup();
  dma.enable();

  I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
  dma.attachInterrupt(isr);
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1052__)
  CORE_PIN6_CONFIG  = 3;  //1:TX_DATA0
#elif defined(__IMXRT1062__)
  CORE_PIN7_CONFIG  = 3;  //1:TX_DATA0
#endif

  const int nbytes_mlno = 2 * 8; // 16 Bytes per minor loop

  dma.TCD->SADDR = ADAT_tx_buffer;
  dma.TCD->SOFF = 4;
  dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
  dma.TCD->NBYTES_MLNO = nbytes_mlno;
  dma.TCD->SLAST = -sizeof(ADAT_tx_buffer);
  dma.TCD->DADDR = &I2S1_TDR0; // TEENSY 4.x I2S1
  dma.TCD->DOFF = 0;
  dma.TCD->CITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
  dma.TCD->DLASTSGA = 0;
  dma.TCD->BITER_ELINKNO = sizeof(ADAT_tx_buffer) / nbytes_mlno;
  dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
  dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); // TEENSY 4.x I2S1
  update_responsibility = update_setup();
  dma.enable();

  // I2S1_RCSR |= I2S_RCSR_RE; // todo: yes we need this but why ?
  I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
  dma.attachInterrupt(isr);
#endif
}

/*

  https://ackspace.nl/wiki/ADAT_project
  https://github.com/xcore/sc_adat/blob/master/module_adat_tx/src/adat_tx_port.xc

  for information about the clock:

  http://cache.freescale.com/files/32bit/doc/app_note/AN4800.pdf

  We need a bitrate twice as high as the SPDIF example.
  Because BITCLK can not be the same as MCLK, but only half of MCLK, we need a 2*MCLK (so for 44100 samplerate we need 88200 MCLK)
*/

void AudioOutputADAT::isr(void)
{
  const int16_t *src1, *src2, *src3, *src4, *src5, *src6, *src7, *src8;
  const int16_t *zeros = (const int16_t *)zerodata;

  uint32_t *end, *dest;
  uint32_t saddr;
  uint32_t sample1, sample2, sample3, sample4, sample5, sample6, sample7, sample8;

  static uint32_t previousnrzi_highlow = 0; //this is used for the NZRI encoding to remember the last state.
  // if the result of the lookup table LSB is other than this lastbit, the result of the lookup table must be inverted.

  //static bool previousframeinverted=false;

  saddr = (uint32_t)(dma.TCD->SADDR);
  dma.clearInterrupt();
  if (saddr < (uint32_t)ADAT_tx_buffer + sizeof(ADAT_tx_buffer) / 2) {
    // DMA is transmitting the first half of the buffer
    // so we must fill the second half
    dest = (uint32_t *)&ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8 / 2];
    end = (uint32_t *)&ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8];
    if (AudioOutputADAT::update_responsibility) AudioStream::update_all();
  } else {
    // DMA is transmitting the second half of the buffer
    // so we must fill the first half
    dest = (uint32_t *)ADAT_tx_buffer;
    end = (uint32_t *)&ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8 / 2];
  }
#if IMXRT_CACHE_ENABLED >= 2
  uint32_t *toFlush = dest;
  uint32_t flushLen = sizeof ADAT_tx_buffer / 2;
#endif

  src1 = (block_ch1_1st) ? block_ch1_1st->data + ch1_offset : zeros;
  src2 = (block_ch2_1st) ? block_ch2_1st->data + ch2_offset : zeros;
  src3 = (block_ch3_1st) ? block_ch3_1st->data + ch3_offset : zeros;
  src4 = (block_ch4_1st) ? block_ch4_1st->data + ch4_offset : zeros;
  src5 = (block_ch5_1st) ? block_ch5_1st->data + ch5_offset : zeros;
  src6 = (block_ch6_1st) ? block_ch6_1st->data + ch6_offset : zeros;
  src7 = (block_ch7_1st) ? block_ch7_1st->data + ch7_offset : zeros;
  src8 = (block_ch8_1st) ? block_ch8_1st->data + ch8_offset : zeros;
  
// SPDIF OUT works now with ADAT
#if IMXRT_CACHE_ENABLED >= 2
  arm_dcache_flush_delete(toFlush, flushLen);
  //asm("dsb":::"memory"); // DO WE NEED THIS ?
#endif // IMXRT_CACHE_ENABLED >= 2

  //Non-NZRI encoded 'empty' ADAT Frame
  /*
    (dest+0) = 0b10000100001000010000100001000010; // bit 0-31
    (dest+1) = 0b00010000100001000010000100001000; // bit 32-63
    (dest+2) = 0b01000010000100001000010000100001; // bit 64-95
    (dest+3) = 0b00001000010000100001000010000100; // bit 96-127

    (dest+4) = 0b00100001000010000100001000010000; // bit 128-159
    (dest+5) = 0b10000100001000010000100001000010; // bit 160-191
    (dest+6) = 0b00010000100001000010000100001000; // bit 192-223
    (dest+7) = 0b01000010000100001000000000010000; // bit 224-255

    dest+=8;

  */

  /*
    //NZRI encoded 'empty' ADAT Frame

    (dest+0) = 0b11111000001111100000111110000011; // bit 0-31
    (dest+1) = 0b11100000111110000011111000001111; // bit 32-63
    (dest+2) = 0b10000011111000001111100000111110; // bit 64-95
    (dest+3) = 0b00001111100000111110000011111000; // bit 96-127

    (dest+4) = 0b00111110000011111000001111100000; // bit 128-159
    (dest+5) = 0b11111000001111100000111110000011; // bit 160-191
    (dest+6) = 0b11100000111110000011111000001111; // bit 192-223
    (dest+7) = 0b10000011111000001111111111100000; // bit 224-255

    dest+=8;

  */

  do
  {
    sample1 = (*src1++);
    sample2 = (*src2++);
    sample3 = (*src3++);
    sample4 = (*src4++);
    sample5 = (*src5++);
    sample6 = (*src6++);
    sample7 = (*src7++);
    sample8 = (*src8++);

    uint32_t value;
    uint32_t nzrivalue;

    value =         0b10000100001000010000100001000010 /* bit  0-31 */
                    //                b00000000000000001000000000000000 // start of 16 bit sample
                    | ((sample1 << 15) & 0b01111000000000000000000000000000)
                    | ((sample1 << 14) & 0b00000011110000000000000000000000)
                    | ((sample1 << 13) & 0b00000000000111100000000000000000)
                    | ((sample1 << 12) & 0b00000000000000001111000000000000)
                    | ((sample2 >> 15) & 0b00000000000000000000000000000001)
                    ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 0) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00010000100001000010000100001000 /* bit 32-63 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample2 << 17) & 0b11100000000000000000000000000000)
                   | ((sample2 << 16) & 0b00001111000000000000000000000000)
                   | ((sample2 << 15) & 0b00000000011110000000000000000000)
                   | ((sample2 << 14) & 0b00000000000000111100000000000000)
                   | ((sample3 >> 13) & 0b00000000000000000000000000000111)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 1) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;


    value =        0b01000010000100001000010000100001 /* bit 64-95 */
                   //                b00000000000000001000000000000000 // start of 16 bit sample
                   | ((sample3 << 19) & 0b10000000000000000000000000000000)
                   | ((sample3 << 18) & 0b00111100000000000000000000000000)
                   | ((sample3 << 17) & 0b00000001111000000000000000000000)
                   | ((sample3 << 16) & 0b00000000000011110000000000000000)
                   | ((sample4 >> 11) & 0b00000000000000000000000000011110)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 2) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00001000010000100001000010000100 /* bit 96-127 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample4 << 20) & 0b11110000000000000000000000000000)
                   | ((sample4 << 19) & 0b00000111100000000000000000000000)
                   | ((sample4 << 18) & 0b00000000001111000000000000000000)
                   | ((sample5 >> 9 ) & 0b00000000000000000000000001111000)
                   | ((sample5 >> 10) & 0b00000000000000000000000000000011)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 3) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00100001000010000100001000010000 /* bit 128-159 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample5 << 22) & 0b11000000000000000000000000000000)
                   | ((sample5 << 21) & 0b00011110000000000000000000000000)
                   | ((sample5 << 20) & 0b00000000111100000000000000000000)
                   | ((sample6 >> 7 ) & 0b00000000000000000000000111100000)
                   | ((sample6 >> 8 ) & 0b00000000000000000000000000001111)
                   ;
    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 4) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b10000100001000010000100001000010 /* bit 160-191 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample6 << 23) & 0b01111000000000000000000000000000)
                   | ((sample6 << 22) & 0b00000011110000000000000000000000)
                   | ((sample7 >> 5 ) & 0b00000000000000000000011110000000)
                   | ((sample7 >> 6 ) & 0b00000000000000000000000000111100)
                   | ((sample7 >> 7 ) & 0b00000000000000000000000000000001)
                   ;

    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 5) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b00010000100001000010000100001000 /* bit 192-223 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample7 << 25) & 0b11100000000000000000000000000000)
                   | ((sample7 << 24) & 0b00001111000000000000000000000000)
                   | ((sample8 >> 3 ) & 0b00000000000000000001111000000000)
                   | ((sample8 >> 4 ) & 0b00000000000000000000000011110000)
                   | ((sample8 >> 5 ) & 0b00000000000000000000000000000111)
                   ;

    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 6) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    value =        0b01000010000100001000000000010000 /* bit 224-255 */
                   //                b00000000000000001000100010001000 // start of 16 bit sample
                   | ((sample8 << 27) & 0b10000000000000000000000000000000)
                   | ((sample8 << 26) & 0b00111100000000000000000000000000)
                   ;

    nzrivalue = previousnrzi_highlow ^ (LookupTable_firstword[(byte)(value >> 24)] ^ LookupTable_secondword[(byte)(value >> 16)] ^ LookupTable_thirdword[(byte)(value >> 8)] ^ LookupTable_fourthword[(byte)value]);
    *(dest + 7) = nzrivalue;
    previousnrzi_highlow = ((nzrivalue & 0b1) == 0b1) ? ~0U : 0U;

    dest += 8;
  } while (dest < end);
  /*
    block = AudioOutputADAT::block_ch1_1st;
    if (block) {
    offset = AudioOutputADAT::ch1_offset;
    src = &block->data[offset];
    do {

      sample = (uint32_t)*src++;

    } while (dest < end);
    offset += AUDIO_BLOCK_SAMPLES/2;
    if (offset < AUDIO_BLOCK_SAMPLES) {
      AudioOutputADAT::ch1_offset = offset;
    } else {
      AudioOutputADAT::ch1_offset = 0;
      AudioStream::release(block);
      AudioOutputADAT::block_ch1_1st = AudioOutputADAT::block_ch1_2nd;
      AudioOutputADAT::block_ch1_2nd = NULL;
    }
    } else {
    sample = 0;

    do {
      // *(dest+0) = 0b11111000001111100000111110000011; // bit 0-31
      // *(dest+1) = 0b11100000111110000011111000001111; // bit 32-63
      // *(dest+2) = 0b10000011111000001111100000111110; // bit 64-95
      // *(dest+3) = 0b00001111100000111110000011111000; // bit 96-127

      // *(dest+4) = 0b00111110000011111000001111100000; // bit 128-159
      // *(dest+5) = 0b11111000001111100000111110000011; // bit 160-191
      // *(dest+6) = 0b11100000111110000011111000001111; // bit 192-223
      // *(dest+7) = 0b10000011111000001111111111100000; // bit 224-255
      dest+=8;
    } while (dest < end);
    }


    dest -= AUDIO_BLOCK_SAMPLES * 8/2 - 8/2;
    block = AudioOutputADAT::block_ch2_1st;
    if (block) {
    offset = AudioOutputADAT::ch2_offset;
    src = &block->data[offset];

    do {
      sample = *src++;

      // *(dest+0) = 0b00111110000011111000001111100000; // bit 128-159
      // *(dest+1) = 0b11111000001111100000111110000011; // bit 160-191
      // *(dest+2) = 0b11100000111110000011111000001111; // bit 192-223
      // *(dest+3) = 0b10000011111000001111111111100000; // bit 224-255
      dest+=8;
    } while (dest < end);

    offset += AUDIO_BLOCK_SAMPLES/2;
    if (offset < AUDIO_BLOCK_SAMPLES) {
      AudioOutputADAT::ch2_offset = offset;
    } else {
      AudioOutputADAT::ch2_offset = 0;
      AudioStream::release(block);
      AudioOutputADAT::block_ch2_1st = AudioOutputADAT::block_ch2_2nd;
      AudioOutputADAT::block_ch2_2nd = NULL;
    }
    } else {
      // *(dest+0) = 0b00111110000011111000001111100000; // bit 128-159
      // *(dest+1) = 0b11111000001111100000111110000011; // bit 160-191
      // *(dest+2) = 0b11100000111110000011111000001111; // bit 192-223
      // *(dest+3) = 0b10000011111000001111111111100000; // bit 224-255
      dest+=8;
    }*/

  if (block_ch1_1st) {
    if (ch1_offset == 0) {
      ch1_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch1_offset = 0;
      release(block_ch1_1st);
      block_ch1_1st = block_ch1_2nd;
      block_ch1_2nd = NULL;
    }
  }
  if (block_ch2_1st) {
    if (ch2_offset == 0) {
      ch2_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch2_offset = 0;
      release(block_ch2_1st);
      block_ch2_1st = block_ch2_2nd;
      block_ch2_2nd = NULL;
    }
  }
  if (block_ch3_1st) {
    if (ch3_offset == 0) {
      ch3_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch3_offset = 0;
      release(block_ch3_1st);
      block_ch3_1st = block_ch3_2nd;
      block_ch3_2nd = NULL;
    }
  }
  if (block_ch4_1st) {
    if (ch4_offset == 0) {
      ch4_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch4_offset = 0;
      release(block_ch4_1st);
      block_ch4_1st = block_ch4_2nd;
      block_ch4_2nd = NULL;
    }
  }
  if (block_ch5_1st) {
    if (ch5_offset == 0) {
      ch5_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch5_offset = 0;
      release(block_ch5_1st);
      block_ch5_1st = block_ch5_2nd;
      block_ch5_2nd = NULL;
    }
  }
  if (block_ch6_1st) {
    if (ch6_offset == 0) {
      ch6_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch6_offset = 0;
      release(block_ch6_1st);
      block_ch6_1st = block_ch6_2nd;
      block_ch6_2nd = NULL;
    }
  }
  if (block_ch7_1st) {
    if (ch7_offset == 0) {
      ch7_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch7_offset = 0;
      release(block_ch7_1st);
      block_ch7_1st = block_ch7_2nd;
      block_ch7_2nd = NULL;
    }
  }
  if (block_ch8_1st) {
    if (ch8_offset == 0) {
      ch8_offset = AUDIO_BLOCK_SAMPLES / 2;
    } else {
      ch8_offset = 0;
      release(block_ch8_1st);
      block_ch8_1st = block_ch8_2nd;
      block_ch8_2nd = NULL;
    }
  }
/*
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
  // if you dont have this you just get noise! - AJ, https://forum.pjrc.com/threads/60914-ADAT-white-noise-on-Teensy-4-0
  arm_dcache_flush_delete((void *)((uint32_t)dest - sizeof(ADAT_tx_buffer) / 2), sizeof(ADAT_tx_buffer) / 2 );
#endif
*/
}

void AudioOutputADAT::mute_PCM(const bool mute)
{
  //vucp = mute?VUCP_INVALID:VUCP_VALID;
  //#TODO

  // The TMR causes the Transmit Data pin to be tri-stated for the length of each selected
  // word and the transmit FIFO is not read for masked words.
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
  // configure transmitter T 4.x
  // SAI Transmit Mask Register (TMR)
  I2S1_TMR = mute; // 1 mute - 0 unmute
#elif defined(KINETISK)
  // configure transmitter T 3.x
  I2S0_TMR = mute; // 1 mute - 0 unmute
#endif
}

void AudioOutputADAT::update(void)
{

  audio_block_t *block, *tmp;

  block = receiveReadOnly(0); // input 0 = channel 1
  if (block) {
    __disable_irq();
    if (block_ch1_1st == NULL) {
      block_ch1_1st = block;
      ch1_offset = 0;
      __enable_irq();
    } else if (block_ch1_2nd == NULL) {
      block_ch1_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch1_1st;
      block_ch1_1st = block_ch1_2nd;
      block_ch1_2nd = block;
      ch1_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(1); // input 1 = channel 2
  if (block) {
    __disable_irq();
    if (block_ch2_1st == NULL) {
      block_ch2_1st = block;
      ch2_offset = 0;
      __enable_irq();
    } else if (block_ch2_2nd == NULL) {
      block_ch2_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch2_1st;
      block_ch2_1st = block_ch2_2nd;
      block_ch2_2nd = block;
      ch2_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(2); // channel 3
  if (block) {
    __disable_irq();
    if (block_ch3_1st == NULL) {
      block_ch3_1st = block;
      ch3_offset = 0;
      __enable_irq();
    } else if (block_ch3_2nd == NULL) {
      block_ch3_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch3_1st;
      block_ch3_1st = block_ch3_2nd;
      block_ch3_2nd = block;
      ch3_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(3); // channel 4
  if (block) {
    __disable_irq();
    if (block_ch4_1st == NULL) {
      block_ch4_1st = block;
      ch4_offset = 0;
      __enable_irq();
    } else if (block_ch4_2nd == NULL) {
      block_ch4_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch4_1st;
      block_ch4_1st = block_ch4_2nd;
      block_ch4_2nd = block;
      ch4_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(4); // channel 5
  if (block) {
    __disable_irq();
    if (block_ch5_1st == NULL) {
      block_ch5_1st = block;
      ch5_offset = 0;
      __enable_irq();
    } else if (block_ch5_2nd == NULL) {
      block_ch5_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch5_1st;
      block_ch5_1st = block_ch5_2nd;
      block_ch5_2nd = block;
      ch5_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(5); // channel 6
  if (block) {
    __disable_irq();
    if (block_ch6_1st == NULL) {
      block_ch6_1st = block;
      ch6_offset = 0;
      __enable_irq();
    } else if (block_ch6_2nd == NULL) {
      block_ch6_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch6_1st;
      block_ch6_1st = block_ch6_2nd;
      block_ch6_2nd = block;
      ch6_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(6); // channel 7
  if (block) {
    __disable_irq();
    if (block_ch7_1st == NULL) {
      block_ch7_1st = block;
      ch7_offset = 0;
      __enable_irq();
    } else if (block_ch7_2nd == NULL) {
      block_ch7_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch7_1st;
      block_ch7_1st = block_ch7_2nd;
      block_ch7_2nd = block;
      ch7_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
  block = receiveReadOnly(7); // channel 8
  if (block) {
    __disable_irq();
    if (block_ch8_1st == NULL) {
      block_ch8_1st = block;
      ch8_offset = 0;
      __enable_irq();
    } else if (block_ch8_2nd == NULL) {
      block_ch8_2nd = block;
      __enable_irq();
    } else {
      tmp = block_ch8_1st;
      block_ch8_1st = block_ch8_2nd;
      block_ch8_2nd = block;
      ch8_offset = 0;
      __enable_irq();
      release(tmp);
    }
  }
}
#endif

#ifndef MCLK_SRC
#if F_CPU >= 20000000
#define MCLK_SRC  3  // the PLL
#else
#define MCLK_SRC  0  // system clock
#endif
#endif

#if defined(KINETISK)
// Teensy 3.x simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT::set_Freq(int _FREQ)
{
  // TODO: should we set & clear the I2S_TCSR_SR bit here?
  setI2SFreq(_FREQ * 2); // TODO; test this on T3.x
  // https://forum.pjrc.com/threads/42092-Audio-Sample-Rate-how-to-change-from-44100-Hz-(44117-65-Hz)-to-48000
}

void AudioOutputADAT::config_ADAT(void)
{
  SIM_SCGC6 |= SIM_SCGC6_I2S;
  SIM_SCGC7 |= SIM_SCGC7_DMA;
  SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

  // enable MCLK output
  I2S0_MCR = I2S_MCR_MICS(MCLK_SRC) | I2S_MCR_MOE;
  //while (I2S0_MCR & I2S_MCR_DUF) ;
  //I2S0_MDR = I2S_MDR_FRACT((MCLK_MULT-1)) | I2S_MDR_DIVIDE((MCLK_DIV-1));

  AudioOutputADAT::setI2SFreq(88200); // (default)

  // configure transmitter
  I2S0_TMR = 0; // SAI Transmit Mask Register (TMR)
  I2S0_TCR1 = I2S_TCR1_TFW(1);  // watermark
  I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
  I2S0_TCR3 = I2S_TCR3_TCE;

  //8 Words per Frame 32 Bit Word-Length -> 256 Bit Frame-Length, MSB First:
  I2S0_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
  I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

  I2S0_RCSR = 0;

#if 0
  // configure pin mux for 3 clock signals (debug only)
  CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
  CORE_PIN9_CONFIG  = PORT_PCR_MUX(6); // pin  9, PTC3, I2S0_TX_BCLK
  //  CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
#endif
}

/*

  https://forum.pjrc.com/threads/38753-Discussion-about-a-simple-way-to-change-the-sample-rate

*/

void AudioOutputADAT::setI2SFreq(int freq) {
  typedef struct {
    uint8_t mult;
    uint16_t div;
  } tmclk;

  const int numfreqs = 14;
  const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int) 44117.64706 , 48000, 88200, (int) (44117.64706 * 2.0), 96000, 176400, (int) (44117.64706 * 4.0), 192000};

#if (F_PLL==16000000)
  const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
  const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
  const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
  const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
  const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==168000000)
  const tmclk clkArr[numfreqs] = {{32, 2625}, {21, 1250}, {64, 2625}, {21, 625}, {128, 2625}, {42, 625}, {8, 119}, {64, 875}, {84, 625}, {16, 119}, {128, 875}, {168, 625}, {32, 119}, {189, 646} };
#elif (F_PLL==180000000)
  const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
  const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
  const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
  const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#elif (F_PLL==256000000)
  // TODO: fix these...
  const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif

  for (int f = 0; f < numfreqs; f++) {
    if ( freq == samplefreqs[f] ) {
      while (I2S0_MCR & I2S_MCR_DUF) ;
      I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
      return;
    }
  }
}
#endif

#if defined(__IMXRT1052__) || defined(__IMXRT1062__)

// Teensy 4.x simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT::set_Freq(int _FREQ)
{
  // TODO: should we set & clear the I2S_TCSR_SR bit here?
  setI2SFreq(_FREQ * 2); // for ADAT we need (MCLK @24,576,000Mhz 48000kHz) or (MCLK @22,579,200Mhz 44100kHz)
}

void AudioOutputADAT::config_ADAT(void)
{
  // Set this to double the audio samplerate for adat to work! - AJ
  int fs = AUDIO_SAMPLE_RATE_EXACT * 2; // (default)

  CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);

  // if either transmitter or receiver is enabled, do nothing /AAA from output_i2s
  if (I2S1_TCSR & I2S_TCSR_TE) return;
  if (I2S1_RCSR & I2S_RCSR_RE) return;

  AudioOutputADAT::setI2SFreq(fs); // set PLL4, CCM_ANALOG
 
  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);

  #if ENABLE_PLL5_VIDEO
  // clear SAI1_CLK register locations
  CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
               | CCM_CSCMR1_SAI1_CLK_SEL(1); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
               | CCM_CS1CDR_SAI1_CLK_PRED(n1 / 2 - 1) // &0x07
               | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
#else
  // clear SAI1_CLK register locations
  CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
               | CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
               | CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
               | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
#endif  // ENABLE_PLL5_VIDEO

  //Select SAI1_MCLK1
  IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
                    | (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //
// SAI1 MCLK1 source select
// 000 ccm.ssi1_clk_root
// 001 ccm.ssi2_clk_root
// 010 ccm.ssi3_clk_root
// 011 iomux.sai1_ipg_clk_sai_mclk
// 100 iomux.sai2_ipg_clk_sai_mclk
// 101 iomux.sai3_ipg_clk_sai_mclk
// 110 Reserved
// 111 Reserved

  // int rsync = 1; // 1b - Synchronous with receiver
  int tsync = 0; // transmitter -- 0b - Asynchronous mode, 1b - Synchronous with receiver.

  // configure transmitter
  I2S1_TMR = 0; // SAI Transmit Mask Register (TMR)
  I2S1_TCR1 = I2S_TCR1_RFW(4);  //was 0, supposed to be: I2S_TCR1_TFW(n), Transmit FIFO Watermark
  I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0); 
  I2S1_TCR3 = I2S_TCR3_TCE; // Enable

  // i want.. 8 Words per Frame 32 Bit Word-Length -> 256 Bit Frame-Length, MSB First:
  I2S1_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
  I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);

  // configure receiver, ADAT IN not implemented yet.
  I2S1_RCSR = 0; // disabled
  /*
    I2S1_RMR = 0;
    //I2S1_RCSR = (1<<25); //Reset
    //I2S1_RCR1 = I2S_RCR1_RFW(0);

      // should this be I2S_TCR4_FRSZ(7) as above? both 3 and 7 work in both places - AJ
    I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
    I2S1_RCR3 = I2S_RCR3_RCE;

    I2S1_RCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
    I2S1_RCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
  */

#if 0
  //debug only:
  CORE_PIN23_CONFIG = 3;  //1:MCLK  22,579,200Mhz--@44100kHz, 24,576,000Mhz--@48000kHz
  CORE_PIN21_CONFIG = 3;  //1:RX_BCLK 5.6 MHz
  CORE_PIN20_CONFIG = 3;  //1:RX_SYNC 44.1 KHz
  //  CORE_PIN6_CONFIG  = 3;  //1:TX_DATA0
  //  CORE_PIN7_CONFIG  = 3;  //1:RX_DATA0
#endif
}

void AudioOutputADAT::setI2SFreq(int freq) {  // PLL4, CCM_ANALOG
  // PLL between 27*24 = 648MHz und 54*24=1296MHz
  int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
  int n2 = 1 + (24000000 * 27) / (freq * 256 * n1);
  double C = ((double)freq * 256 * n1 * n2) / 24000000;
  int c0 = C;
  int c2 = 10000;
  int c1 = C * c2 - (c0 * c2);

#if ENABLE_PLL5_VIDEO  // - Set PLL5 CCM_ANALOG_PLL_VIDEO
 	//if (!force && (CCM_ANALOG_PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_ENABLE)) return;
	CCM_ANALOG_PLL_VIDEO = CCM_ANALOG_PLL_VIDEO_BYPASS | CCM_ANALOG_PLL_VIDEO_ENABLE
			     | CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
			     | CCM_ANALOG_PLL_VIDEO_DIV_SELECT(c0);

	CCM_ANALOG_PLL_VIDEO_NUM   = c1 & CCM_ANALOG_PLL_VIDEO_NUM_MASK;
	CCM_ANALOG_PLL_VIDEO_DENOM = c2 & CCM_ANALOG_PLL_VIDEO_DENOM_MASK;
	
	CCM_ANALOG_PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_POWERDOWN;//Switch on PLL
	while (!(CCM_ANALOG_PLL_VIDEO & CCM_ANALOG_PLL_VIDEO_LOCK)) {}; // Wait for pll-lock

    // -- PLL5 b10, Dec2, -- divide by 1
    // -- PLL5 b01, Dec1, -- divide by 2
	CCM_ANALOG_MISC2 = CCM_ANALOG_MISC2_VIDEO_DIV(1); // divide by 2
	
	CCM_ANALOG_PLL_VIDEO &= ~CCM_ANALOG_PLL_VIDEO_BYPASS;// Disable Bypass 
	  // CCM SAI1
    CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
               | CCM_CS1CDR_SAI1_CLK_PRED(n1 / 2- 1) // &0x07
               | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
#else
  // set PLL4 CCM_ANALOG_PLL_AUDIO
  set_audioClock(c0, c1, c2, true); // see \libraries\Audio\utility\imxrt_hw.ccp
    // CCM SAI1
  CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
               | CCM_CS1CDR_SAI1_CLK_PRED(n1 - 1) // &0x07
               | CCM_CS1CDR_SAI1_CLK_PODF(n2 - 1); // &0x3f
#endif  // ENABLE_PLL5_VIDEO

#if 0
  //debug only:
  Serial.printf("SetI2SFreq(%d)\n", freq);
  //Serial.print("Freq "),Serial.println(freq);
  Serial.print("c0 DIV   "), Serial.println(c0);
  Serial.print("c1 NUM   "), Serial.println(c1);
  Serial.print("c2 DENOM "), Serial.println(c2);
  Serial.print("n1 PRED/ "), Serial.println(n1);
  Serial.print("n2 PODF/ "), Serial.println(n2);
  Serial.println();
#endif
#endif
}

#if defined(KINETISL)
// Fix compile error on Teensy LC

void AudioOutputADAT::mute_PCM(const bool mute)
{
  // Teensy LC return
}

// simple way to change the samplerate of ADAT at runtime.
void AudioOutputADAT::set_Freq(int _FREQ)
{
  // Teensy LC return
}

void AudioOutputADAT::begin(void)
{
  // Teensy LC return
}

void AudioOutputADAT::update(void)
{

  audio_block_t *block;
  block = receiveReadOnly(0); // input 0 = ch1 channel
  if (block) release(block);
  block = receiveReadOnly(1); // input 1 = ch2 channel
  if (block) release(block);
  block = receiveReadOnly(2); // input 2 = ch3 channel
  if (block) release(block);
  block = receiveReadOnly(3); // input 3 = ch4 channel
  if (block) release(block);
  block = receiveReadOnly(4); // input 4 = ch5 channel
  if (block) release(block);
  block = receiveReadOnly(5); // input 5 = ch6 channel
  if (block) release(block);
  block = receiveReadOnly(6); // input 6 = ch7 channel
  if (block) release(block);
  block = receiveReadOnly(7); // input 7 = ch8 channel
  if (block) release(block);
}
#endif

ADAT1 SAI1 -- no changes.
output_adat.h
Code:
/* ADAT for Teensy 3.X
 * Copyright (c) 2017, Ernstjan Freriks,
 * Thanks to Frank Bösing & KPC & Paul Stoffregen!
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef output_ADAT_h_
#define output_ADAT_h_

#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"

class AudioOutputADAT : public AudioStream
{
public:
	AudioOutputADAT(void) : AudioStream(8, inputQueueArray) { begin(); }
	virtual void update(void);
	void begin(void);
	static void mute_PCM(const bool mute);
	void set_Freq(int freq); // change the samplerate of ADAT at runtime, 44100 or 48000 only!!!
protected:
	AudioOutputADAT(int dummy): AudioStream(8, inputQueueArray) {}
	static void config_ADAT(void);
	static audio_block_t *block_ch1_1st;
	static audio_block_t *block_ch2_1st;
	static audio_block_t *block_ch3_1st;
	static audio_block_t *block_ch4_1st;
	static audio_block_t *block_ch5_1st;
	static audio_block_t *block_ch6_1st;
	static audio_block_t *block_ch7_1st;
	static audio_block_t *block_ch8_1st;
	static bool update_responsibility;
	static DMAChannel dma;
	static void isr(void);
	static void setI2SFreq(int freq);
private:
	//static uint32_t vucp;
	static audio_block_t *block_ch1_2nd;
	static audio_block_t *block_ch2_2nd;
	static audio_block_t *block_ch3_2nd;
	static audio_block_t *block_ch4_2nd;
	static audio_block_t *block_ch5_2nd;
	static audio_block_t *block_ch6_2nd;
	static audio_block_t *block_ch7_2nd;
	static audio_block_t *block_ch8_2nd;
	static uint16_t ch1_offset;
	static uint16_t ch2_offset;
	static uint16_t ch3_offset;
	static uint16_t ch4_offset;
	static uint16_t ch5_offset;
	static uint16_t ch6_offset;
	static uint16_t ch7_offset;
	static uint16_t ch8_offset;
	audio_block_t *inputQueueArray[8];
};

#endif
;)
 
Back
Top