ADAT white noise on Teensy 4.0

You could make compiler directives to sort out the code:
Code:
#if defined(__IMXRT1062__)
// put all the T4.x code here
#endif
#if defined(KINETISK)
// put all the T3.x code here
#endif
This way you won't break anything for the T3.x and have a version that works on T4.x You could also use these to "weave" the code one into the other and reuse parts that are common for both, but at a cost of readability.
Or call yours ADAT_T4, so the user can choose, but at the cost of code portability.

BTW: I have access to professional ADAT gear, so I could test the code if you post it here
 
Hi Paul, I would be up for that. Probably a link to my forum profile here would be best if people need help with it. Before we do that though lets see if I can fix the code so that it works for T3 as well as T4.
I will send you an email with my address so that you can send me out a T3 for testing.

Thanks Deleted User, I have actually tried to do that for most of the code but some of the code that needed surrounding was already an if statement itself so the directives were not working as I expected. I am sure I could fix this, I will give this a bit more of a soak test and take Paul up on his kind offer of some T3.X hardware and then I will send over some code!

Thanks all : )
 
I've been working on ADAT as well (continuing a work of radias), but stumbled with that dreaded cache management and then hit a wall with PLL frequency for 48kHz.
I am very curious to see how you made it.
 
It's only working at 44.1k at the moment! I am yet to get 48k working, I would really like to. Maybe you can help? Here is the code working at 44.1k...

Code:
//NOTE: THIS CODE WILL NO LONGER WORK ON Teensy 3.X

/* 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.
 */

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


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

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;
//uint32_t  AudioOutputADAT::vucp = VUCP_VALID;

DMAMEM static uint32_t ADAT_tx_buffer[AUDIO_BLOCK_SAMPLES * 8]; //4 KB, AUDIO_BLOCK_SAMPLES is usually 128
//DMAMEM __attribute__((aligned(32))) //AAA
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;
	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);
	update_responsibility = update_setup();
	dma.enable();

	I2S1_RCSR |= I2S_RCSR_RE;
	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];
	}

	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);
	/*
	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 you dont have this you just get noise! - AJ
    arm_dcache_flush_delete((void *)((uint32_t)dest - sizeof(ADAT_tx_buffer) / 2), sizeof(ADAT_tx_buffer) / 2 );
    
}

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

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);
		}
	}
}


#if defined(KINETISK)
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
  // PLL is at 96 MHz in these modes
  #define MCLK_MULT 2
  #define MCLK_DIV  17
#elif F_CPU == 72000000
  #define MCLK_MULT 8
  #define MCLK_DIV  51
#elif F_CPU == 120000000
  #define MCLK_MULT 8
  #define MCLK_DIV  85
#elif F_CPU == 144000000
  #define MCLK_MULT 4
  #define MCLK_DIV  51
#elif F_CPU == 168000000
  #define MCLK_MULT 8
  #define MCLK_DIV  119
#elif F_CPU == 180000000
  #define MCLK_MULT 16
  #define MCLK_DIV  255
  #define MCLK_SRC  0
#elif F_CPU == 192000000
  #define MCLK_MULT 1
  #define MCLK_DIV  17
#elif F_CPU == 216000000
  #define MCLK_MULT 12
  #define MCLK_DIV  17
  #define MCLK_SRC  1
#elif F_CPU == 240000000
  #define MCLK_MULT 2
  #define MCLK_DIV  85
  #define MCLK_SRC  0
#elif F_CPU == 256000000
  #define MCLK_MULT 12
  #define MCLK_DIV  17
  #define MCLK_SRC  1
#elif F_CPU == 16000000
  #define MCLK_MULT 12
  #define MCLK_DIV  17
#else
  #error "This CPU Clock Speed is not supported by the Audio library";
#endif

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

PROGMEM
void AudioOutputADAT::config_ADAT(void)
{
#if defined(KINETISK)
	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);

	// configure transmitter
	I2S0_TMR = 0;
	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)	44.1kHz
	CORE_PIN9_CONFIG  = PORT_PCR_MUX(6); // pin  9, PTC3, I2S0_TX_BCLK		5.6 MHz
	CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK			11.43MHz
#endif

#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
    
    

	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;
    
//PLL:
    
	int fs = 88200; // Set this to double the audio samplerate for adat to work! - AJ
    
	// 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);

	double C = ((double)fs * 256 * n1 * n2) / 24000000;
	int c0 = C;
	int c2 = 10000;
	int c1 = C * c2 - (c0 * c2);
	set_audioClock(c0, c1, c2);

	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 MCLK
	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 = 0;
	int tsync = 1;
    
	// configure transmitter
	I2S1_TMR = 0;
	I2S1_TCR1 = I2S_TCR1_RFW(0);  // watermark
	I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
	I2S1_TCR3 = I2S_TCR3_TCE;

    // i want.. 8 Words per Frame 32 Bit Word-Length -> 256 Bit Frame-Length, MSB First:
    // in T3.X code this was 7 so using that here.. - AJ
	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);

	//I2S1_RCSR = 0;
	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(3) | 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);
    
    //AudioOutputADAT::setI2SFreq(88200);

#if 0
	//debug only:
	CORE_PIN23_CONFIG = 3;  //1:MCLK	11.43MHz
	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
}

/*

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

*/



void AudioOutputADAT::setI2SFreq(int freq) {
     // 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);
  set_audioClock(c0, c1, c2, true);
  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 
Serial.printf("SetI2SFreq(%d)\n",freq);
} 

#elif defined(KINETISL)

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

In the fullness of time it would be really great to get the Teensy to run as a word clock slave ..and 24bit output
My RME Fireface picking up the incoming ADAT sync:

Screen Shot 2020-05-17 at 14.21.49.png

T4 >> ADAT >> RME in action: https://photos.app.goo.gl/YpRTj62V3yZTrv2DA

Win
 
Last edited:
Sound great!


48k:Should be simple..

AudioOutputADAT::setI2SFreq(88200);

replace it with AudioOutputADAT::setI2SFreq( 2 * AUDIO_SAMPLE_RATE_EXACT);

After that it will use the freqency of the library.
It's better to do it this way.


You can set the library-frequency in AudioStream.h:

#define AUDIO_SAMPLE_RATE_EXACT 44100.0f //<- change to 48000.0f

But be aware that not the entire lib will work. Not all parts of it support that!
But I - hope - most parts will make no problems.
 
Thanks, AudioOutputADAT::setI2SFreq(88200); is actually meant to be commented out in the code above, I have updated it so that it is. (it works either way)

Code:
int fs = 88200; // Set this to double the audio samplerate for adat to work! - AJ

this is the bit that needs setting to 88200, I will update it to "2 * AUDIO_SAMPLE_RATE_EXACT" as suggested and see if 48 will work
 
Thanks. Now I'll take apart some older projects to "release" a T3.6 and a T4.0 to do some testing.
(The T4.1 beta is still creeping around some post facilities. Caustic that.)
The 48k was because some equipment only accept this as input.
I'd hope to get the Teensy sync to wordclock, to make digital audio I/O usable.
Using Teensy as the master clock is actually not a good idea, because of obviously audible jitter.
 
Deleted User, how large is the jitter? Have you measured it?
alrj, do you hear jitter, too?


Anyway, i think even the cheapest SPDIF receivers have buffers (edit: together with a PLL) nowadays..
And I guess it's the same for ADAT.

I can't hear any jitter.

...apart from that, it should be possible to use all this as "slave" if you provide a MCLK.
 
Well, it shows if you feed the digital S/PDIF (that is otherwise working well, btw) with higher frequency audio into a DAW via DAC >> ADC. Or if you feed S/PDIF directly into DAW with sync to S/PDIF and playback at internal clock. There is this flanging effect.
 
totally agree Deleted User that is why I said "it would be really great to get the Teensy to run as a word clock slave" then my RME can be the master and send a word clock signal to the Teensy for rock solid clocking. To be fair I cant hear any huge issues with the RME taking the clock from the ADAT signal at the moment. But if you have a decent clock.. might as well use it.

Am I right in thinking that AudioPlaySdWav will not play 48k wav files? that is a shame as obviously 44.1k wavs play at the wrong speed (and therefore pitch) at 48k :D
 
Sorted it!

I added...

Code:
 } else if (rate == 48000) {
		b2m = B2M_48000;

to play_sd_wav.cpp

edit: and this line just above...

Code:
#define B2M_48000 (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT) // 97352592
 
There is jitter for sure, you can hear a slight loss of definition on the top end. I am sure this can be cured with a word clock, Deleted User do you know how to implement this?

Thanks again for the input everyone

edit: actually if I record the ADAT in, (file on the SD on the teensy) into my DAW, put it on the timeline next to the original clip and the flip the phase it totally cancels out so any perception of jitter must just be placebo.
 
Last edited:
Well, I found no source for the imx.rt - but some time ago I read a document where it was measured for a predecessor-chip (was it imx6?) - it was around 50ps.
I would be surprised if the imx.rt was much worse.
Someone should measure it sometime. But it's not so easy, in the picosecond range.

Then: Flanging is not jitter. That's a completely different thing., and unavoidable with independent oscillators.

So, just add a way to make the T4 a slave.
 
Last edited:
edit: actually if I record the ADAT in, (file on the SD on the teensy) into my DAW, put it on the timeline next to the original clip and the flip the phase it totally cancels out so any perception of jitter must just be placebo.
Yes, with this you'd actually not get any arror, no matter how far clocks are off.

Yes, wording issue. Jitter is unstable playback or recording speed in audio, while it is this timing imperfection between single clock cycles in electronics.

edit: Here is a nice page about it: https://headfonics.com/2017/12/what-is-jitter-in-audio/
 
as said, feed mclk to the teensy.
Not sure, should be in the register manual somewhere - it might be possible to use the SPDI-IN as clocksource, too(?)
I am completely unsure about this - but I could imagine that it is possible.
 
Yes, with this you'd actually not get any arror, no matter how far clocks are off.

Yup I realised that afterwards and ran the test again without the sync to the ADAT on, as expected it didn't phase cancel but it really isn't far off.

Do you know how we might send mclk to the teensy from a BNC wordclock Deleted User? I could look into it but no point both of us doing it.
 
I wanted to test exactly if S/PDIF in could work as a word clock as soon as I get an other Teensy 4 or the 4.1 beta.
Actually, if you omit the timecode signal coded into the frames data, a word clock is just a AES or S/PDIF signal. The clock phase and frequency is derived from the bitstream itself.
 
If you'd use the SPDIF-Hardware instead of I2S it would be doable, i think. As said, I'm not sure if it possible to sync I2S and the SPDIF-device.. but chances are higher if both, in- and out use the SPDIF hw. and keep I2S out of the equation.
 
If you look at the images I have posted above from my RME interface you can see that it is locking to the clock in the ADAT stream. it even says "Lock" or "Sync" as part of the status.

We want to drive the teensy from a wordclock on the target audio interface. in my case that is the RME which has BNC word clock out. So I think the question is, how to turn that accurate BNC word clock out into mclk to feed the teensy.

edit: Frank just so I am clear are you suggesting we might be able to send the word clock back to a spdif input on the teensy?
 
Thanks Frank! I think that is something for another day, or perhaps something you might be up for Deleted User. I am going to push the rest of this project forwards a bit!
 
Back
Top