Forum Rule: Always post complete source code & details to reproduce any issue!
Page 3 of 3 FirstFirst 1 2 3
Results 51 to 74 of 74

Thread: ADAT white noise on Teensy 4.0

  1. #51
    Deleted User
    Guest
    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 by Deleted User; 05-17-2020 at 04:21 PM.

  2. #52
    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.

  3. #53
    Deleted User
    Guest
    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 by Deleted User; 05-17-2020 at 04:44 PM.

  4. #54
    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.

  5. #55
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Posts
    854
    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.

  6. #56
    Deleted User
    Guest
    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!

  7. #57
    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

  8. #58
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,735
    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...

  9. #59
    I don't totally follow what you are saying I am afraid Frank!

  10. #60
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,735
    It looks like we need the bitclock only.

  11. #61
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Posts
    854
    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.

  12. #62
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,735
    Ah.. I see

  13. #63
    Deleted User
    Guest
    Frame sync?

    BNC word clock is the same as LRCLK.

  14. #64
    Senior Member
    Join Date
    Apr 2014
    Location
    -
    Posts
    9,735
    That's the same for two channels.

  15. #65
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Posts
    854
    Quote Originally Posted by Deleted User View Post
    Frame sync?

    BNC word clock is the same as LRCLK.
    It has many names, frame sync is one of them and it’s what the manual uses.

  16. #66
    Senior Member
    Join Date
    Dec 2013
    Location
    East Stroudsburg PA.
    Posts
    339
    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.

  17. #67
    Senior Member
    Join Date
    Dec 2013
    Location
    East Stroudsburg PA.
    Posts
    339

    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.
    Click image for larger version. 

Name:	T_30.jpg 
Views:	11 
Size:	156.5 KB 
ID:	30687
    I guess I need to find something newer

  18. #68
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,995
    Quote Originally Posted by Chris O. View Post
    I guess I need to find something newer
    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.

  19. #69
    Senior Member
    Join Date
    Dec 2013
    Location
    East Stroudsburg PA.
    Posts
    339
    I tried to use less samples that was easy.
    \libraries\Audio\examples\HardwareTesting\ADAT_Dru mSamplePlayer
    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);
    
      // sound3.play(AudioSampleGong);
    
      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 ?
    Click image for larger version. 

Name:	Tech Specifications T3_0 T3_2.jpg 
Views:	9 
Size:	88.6 KB 
ID:	30696

  20. #70
    Senior Member
    Join Date
    Dec 2013
    Location
    East Stroudsburg PA.
    Posts
    339
    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
    Click image for larger version. 

Name:	PLL4 CLOCK.jpg 
Views:	10 
Size:	73.4 KB 
ID:	30697
    Last edited by Chris O.; 03-23-2023 at 11:23 PM. Reason: ADD PIC.

  21. #71
    Senior Member
    Join Date
    Dec 2013
    Location
    East Stroudsburg PA.
    Posts
    339
    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

  22. #72
    Senior Member
    Join Date
    Dec 2013
    Location
    East Stroudsburg PA.
    Posts
    339
    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

  23. #73
    Senior Member
    Join Date
    Dec 2013
    Location
    East Stroudsburg PA.
    Posts
    339
    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). 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.

  24. #74
    Senior Member
    Join Date
    Dec 2013
    Location
    East Stroudsburg PA.
    Posts
    339
    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.
    Click image for larger version. 

Name:	ADAT 16 OUT.jpg 
Views:	13 
Size:	115.1 KB 
ID:	30829

    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •