From b1235121463cdaa5cba23569513909a1fadadec5 Mon Sep 17 00:00:00 2001 From: boblark Date: Fri, 13 May 2022 16:19:06 -0700 Subject: [PATCH 01/15] Addded missing USB_Audio_F32.h reference --- OpenAudio_ArduinoLibrary.h | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenAudio_ArduinoLibrary.h b/OpenAudio_ArduinoLibrary.h index 7928294..2ad4d11 100644 --- a/OpenAudio_ArduinoLibrary.h +++ b/OpenAudio_ArduinoLibrary.h @@ -54,3 +54,4 @@ #include "RadioFMDetector_F32.h" #include "radioNoiseBlanker_F32.h" #include "synth_sin_cos_f32.h" +#include "USB_Audio_F32.h" From a8a82af2a5c305f511510fe802ed9b744ba8a9ce Mon Sep 17 00:00:00 2001 From: boblark Date: Fri, 13 May 2022 16:58:30 -0700 Subject: [PATCH 02/15] removed conditional declare --- AudioAlignLR_F32.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/AudioAlignLR_F32.cpp b/AudioAlignLR_F32.cpp index 2c07fd0..13f01c6 100644 --- a/AudioAlignLR_F32.cpp +++ b/AudioAlignLR_F32.cpp @@ -29,7 +29,7 @@ #include "AudioAlignLR_F32.h" -void AudioAlignLR_F32::update(void) { static uint32_t nnn = 0; +void AudioAlignLR_F32::update(void) { audio_block_f32_t *block_L,*block_R; audio_block_f32_t *block2_L,*block2_R; audio_block_f32_t *blockOutTestSignal; @@ -64,18 +64,15 @@ void AudioAlignLR_F32::update(void) { static uint32_t nnn = 0; return; } - if(currentTPinfo.TPsignalHardware == TP_SIGNAL_CODEC) - { - blockOutTestSignal = AudioStream_F32::allocate_f32(); - if (!blockOutTestSignal) - { - AudioStream_F32::release(block_L); - AudioStream_F32::release(block_R); - AudioStream_F32::release(block2_L); - AudioStream_F32::release(block2_R); - return; - } - } + blockOutTestSignal = AudioStream_F32::allocate_f32(); + if(currentTPinfo.TPsignalHardware==TP_SIGNAL_CODEC && !blockOutTestSignal) + { + AudioStream_F32::release(block_L); + AudioStream_F32::release(block_R); + AudioStream_F32::release(block2_L); + AudioStream_F32::release(block2_R); + return; + } // Input data is now in block_L and block_R. Filter from there to // block2_L and block2_R @@ -211,8 +208,8 @@ void AudioAlignLR_F32::update(void) { static uint32_t nnn = 0; } } AudioStream_F32::transmit(blockOutTestSignal, 2); // NOTE: Goes to third output - AudioStream_F32::release(blockOutTestSignal); } + AudioStream_F32::release(blockOutTestSignal); currentTPinfo.nMeas++; // Serial.println(micros() - t0); // for timing From 4dc97159617b7c16b1dbc24ed26b556677c43dec Mon Sep 17 00:00:00 2001 From: boblark Date: Fri, 13 May 2022 17:47:56 -0700 Subject: [PATCH 03/15] removed conditional declare --- RadioIQMixer_F32.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/RadioIQMixer_F32.cpp b/RadioIQMixer_F32.cpp index 580d6fc..1bb0c42 100644 --- a/RadioIQMixer_F32.cpp +++ b/RadioIQMixer_F32.cpp @@ -38,14 +38,18 @@ void RadioIQMixer_F32::update(void) { if (!blockIn0) return; - if(twoChannel) { - blockIn1 = AudioStream_F32::receiveWritable_f32(1); - if (!blockIn1) { - AudioStream_F32::release(blockIn0); - if(errorPrintIQM) Serial.println("IQMIXER-ERR: No 1 input memory"); - return; - } - } + if(twoChannel) + { + blockIn1 = AudioStream_F32::receiveWritable_f32(1); + if (!blockIn1) + { + AudioStream_F32::release(blockIn0); + if(errorPrintIQM) Serial.println("IQMIXER-ERR: No 1 input memory"); + return; + } + } + else + blockIn1 = NULL; // Try to get a pair of blocks for the IQ output blockOut_i = AudioStream_F32::allocate_f32(); From f46914a95864f567b82ce2b7993393a30cd1a63f Mon Sep 17 00:00:00 2001 From: boblark Date: Fri, 13 May 2022 21:37:47 -0700 Subject: [PATCH 04/15] Corrected missing power calculation --- analyze_tonedetect_F32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyze_tonedetect_F32.cpp b/analyze_tonedetect_F32.cpp index 5e84cae..86affea 100644 --- a/analyze_tonedetect_F32.cpp +++ b/analyze_tonedetect_F32.cpp @@ -112,7 +112,7 @@ AudioAnalyzeToneDetect_F32::operator bool() { q2 = out2; len = length; __enable_irq(); - + power = q1*q1 + q2*q2 - q1*q2*coef; trigger = (float)len * thresh; trigger *= trigger; //Serial.println("bool: power, trigger = "); Serial.print(power, 6); Serial.print(", "); Serial.println(trigger, 6); From bd14b2b8855c7f1963ea3a4d3499db7de05b5f13 Mon Sep 17 00:00:00 2001 From: boblark Date: Fri, 13 May 2022 23:33:50 -0700 Subject: [PATCH 05/15] Corrected warnings (no problems) --- radioModulatedGenerator_F32.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/radioModulatedGenerator_F32.cpp b/radioModulatedGenerator_F32.cpp index 73c5864..37bcf8e 100644 --- a/radioModulatedGenerator_F32.cpp +++ b/radioModulatedGenerator_F32.cpp @@ -34,12 +34,14 @@ void radioModulatedGenerator_F32::update(void) { float32_t a, b, deltaPhase, phaseC, amSig; // Input 0 is for amplitude modulation. + inAmpl = NULL; if(doAM) { inAmpl = AudioStream_F32::receiveReadOnly_f32(0); if (!inAmpl) return; } // Input 1 is for phase or frequency modulation. + inPhaseFreq = NULL; if(doPM || doFM) { inPhaseFreq = AudioStream_F32::receiveReadOnly_f32(1); if (!inPhaseFreq) { @@ -55,6 +57,7 @@ void radioModulatedGenerator_F32::update(void) { return; } + outBlockQ = NULL; if(bothIQ) { outBlockQ = AudioStream_F32::allocate_f32(); if (!outBlockQ) { From c6c0c3ca31b621f72b95116297335c5a528bfdf9 Mon Sep 17 00:00:00 2001 From: boblark Date: Sat, 14 May 2022 08:28:09 -0700 Subject: [PATCH 06/15] Removed unused variable --- examples/TestFFT4096iqEM/TestFFT4096iqEM.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/TestFFT4096iqEM/TestFFT4096iqEM.ino b/examples/TestFFT4096iqEM/TestFFT4096iqEM.ino index 0d91e17..7646776 100644 --- a/examples/TestFFT4096iqEM/TestFFT4096iqEM.ino +++ b/examples/TestFFT4096iqEM/TestFFT4096iqEM.ino @@ -76,7 +76,6 @@ void setup(void) { void loop(void) { static bool doPrint=true; - float *pPwr; // Print output, once if( FFT4096iqEM1.available() && jj++>2 && doPrint ) { From b222b6049691ff53481d8c8e0e68cc437f522408 Mon Sep 17 00:00:00 2001 From: boblark Date: Sat, 14 May 2022 09:06:41 -0700 Subject: [PATCH 07/15] Corrected syntax to stop warnings --- analyze_fft4096_iqem_F32.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyze_fft4096_iqem_F32.cpp b/analyze_fft4096_iqem_F32.cpp index 5d06f36..0c84f7a 100644 --- a/analyze_fft4096_iqem_F32.cpp +++ b/analyze_fft4096_iqem_F32.cpp @@ -352,7 +352,7 @@ void AudioAnalyzeFFT4096_IQEM_F32::update(void) { // Apply the window function, if any, to the time series. Half size window buffer. - if(wNum!=NULL && pWindow) + if(wNum>NO_WINDOW && pWindow) // fixed syntax 14May2022 RSL { for (int i=0; i < 2048; i++) { *(pFFT_buffer + 2*i) *= *(pWindow + i); // real From 49e16b7876624efb638adaf8676e614225c7a454 Mon Sep 17 00:00:00 2001 From: boblark Date: Sat, 14 May 2022 09:09:42 -0700 Subject: [PATCH 08/15] Corrected interpolation of sine, minor change in values --- RadioFMDetector_F32.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/RadioFMDetector_F32.cpp b/RadioFMDetector_F32.cpp index 81f5c1e..cbfc7f8 100644 --- a/RadioFMDetector_F32.cpp +++ b/RadioFMDetector_F32.cpp @@ -1,7 +1,7 @@ /* * RadioFMDetector_F32.cpp * - * 22 March 2020 + * 25 April 2022 * Bob Larkin, in support of the library: * Chip Audette, OpenAudio, Apr 2017 * ------------------- @@ -57,15 +57,9 @@ void RadioFMDetector_F32::update(void) { return; } - // If there's no coefficient table, give up. - if (fir_IQ_Coeffs == NULL) { - if(errorPrintFM) Serial.println("FMDET-ERR: No IQ FIR Coefficients"); - AudioStream_F32::release(blockIn); - return; - } if (fir_Out_Coeffs == NULL) { - if(errorPrintFM) Serial.println("FMDET-ERR: No Out FIR Coefficients"); + //if(errorPrintFM) Serial.println("FMDET-ERR: No Out FIR Coefficients"); AudioStream_F32::release(blockIn); return; } @@ -73,7 +67,7 @@ void RadioFMDetector_F32::update(void) { // Try to get a block for the FM output blockOut = AudioStream_F32::allocate_f32(); if (!blockOut){ // Didn't have any - if(errorPrintFM) Serial.println("FMDET-ERR: No Output Memory"); + //if(errorPrintFM) Serial.println("FMDET-ERR: No Output Memory"); AudioStream_F32::release(blockIn); return; } From fc27a9f05dce374fd0a84f56568bf592de3fb0ad Mon Sep 17 00:00:00 2001 From: boblark Date: Sat, 14 May 2022 09:59:39 -0700 Subject: [PATCH 09/15] Removed USB_Audio_F32.h as it needs theIDE set to Tools>USB Type>Audio --- OpenAudio_ArduinoLibrary.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenAudio_ArduinoLibrary.h b/OpenAudio_ArduinoLibrary.h index 2ad4d11..29eceed 100644 --- a/OpenAudio_ArduinoLibrary.h +++ b/OpenAudio_ArduinoLibrary.h @@ -54,4 +54,4 @@ #include "RadioFMDetector_F32.h" #include "radioNoiseBlanker_F32.h" #include "synth_sin_cos_f32.h" -#include "USB_Audio_F32.h" +// #include "USB_Audio_F32.h" Include this separately if needed. Then in IDE Tools>USB Type>Audio From a609fa8602e9e81d45369c8d5307af6761b81366 Mon Sep 17 00:00:00 2001 From: boblark Date: Sat, 14 May 2022 10:39:22 -0700 Subject: [PATCH 10/15] Added note about USB_Audio_F32.h --- readme.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/readme.md b/readme.md index 1322107..817a7c7 100644 --- a/readme.md +++ b/readme.md @@ -22,6 +22,11 @@ ready to be used for T3.x and T4.x. There are some restrictions, particularly t **Tympan Project** Many of the classes in this library were put together as part of the [Tympan Project.](https://github.com/Tympan) That is oriented towards open-source hearing aid and hearing aid development tools. It has its own [Tympan Design Tool](https://tympan.github.io/Tympan_Audio_Design_Tool/) as well as some custom Teensy-based hardware. Additionally, there are a few classes in this library that use terminology and variables that are specific to audiology. It is intended that these, in time, be replaced by similar classes with more conventional descriptors. And, of course, if your interest is in hearing aids, you should spend time at the Tympan project! +Notes +----- + +1 - The USB_Audio_F32.h includes all the functions needed to use USB audio for input and output. However, it is not in the OpenAudio_ArduinoLibrary.h as would be expected. To use this class, add "#include USB_Audio_F32.h" to the INO file and before compiling, go to the IDE Tools>USB Type and set the radio button to "Audio." This should then compile without error. Also, using this class requires some amount of I16 audio memory, such as a line in the top of the INO, "AudioMemory(10);" + Installation ------------ From 6a5d40a4595c608bf6576947e05a722acdf946dd Mon Sep 17 00:00:00 2001 From: joerg Date: Sun, 15 May 2022 11:40:43 +0200 Subject: [PATCH 11/15] fixed a typo int -> float -> int conversion is now lossless credits to jcj83429, same as https://github.com/chipaudette/OpenAudio_ArduinoLibrary/pull/8/commits/93a3732fbff4816c4040416533fabeda9974cf99 --- AudioConvert_F32.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AudioConvert_F32.h b/AudioConvert_F32.h index 485e867..bc7f25f 100644 --- a/AudioConvert_F32.h +++ b/AudioConvert_F32.h @@ -36,7 +36,7 @@ class AudioConvert_I16toF32 : public AudioStream_F32 //receive Int and transmits static void convertAudio_I16toF32(audio_block_t *in, audio_block_f32_t *out, int len) { //WEA Method. Should look at CMSIS arm_q15_to_float instead: https://www.keil.com/pack/doc/CMSIS/DSP/html/group__q15__to__x.html#gaf8b0d2324de273fc430b0e61ad4e9eb2 - const float MAX_INT = 32678.0; + const float MAX_INT = 32768.0; for (int i = 0; i < len; i++) out->data[i] = (float)(in->data[i]); arm_scale_f32(out->data, 1.0/MAX_INT, out->data, out->length); //divide by 32678 to get -1.0 to +1.0 } From cce309ffd299c512d32092810b3fd6828844f286 Mon Sep 17 00:00:00 2001 From: joerg Date: Sun, 15 May 2022 11:42:38 +0200 Subject: [PATCH 12/15] have defaults for AudioSettings_F32 --- AudioSettings_F32.cpp | 2 +- AudioSettings_F32.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/AudioSettings_F32.cpp b/AudioSettings_F32.cpp index 195cb69..fb3eefc 100644 --- a/AudioSettings_F32.cpp +++ b/AudioSettings_F32.cpp @@ -1,5 +1,5 @@ - +#include #include "AudioSettings_F32.h" #include "AudioStream_F32.h" diff --git a/AudioSettings_F32.h b/AudioSettings_F32.h index 7832af7..88bdd4a 100644 --- a/AudioSettings_F32.h +++ b/AudioSettings_F32.h @@ -2,9 +2,11 @@ #ifndef _AudioSettings_F32_ #define _AudioSettings_F32_ +#include // for AUDIO_SAMPLE_RATE_EXACT, AUDIO_BLOCK_SAMPLES + class AudioSettings_F32 { public: - AudioSettings_F32(float fs_Hz, int block_size) : + AudioSettings_F32(float fs_Hz=AUDIO_SAMPLE_RATE_EXACT, int block_size=AUDIO_BLOCK_SAMPLES) : sample_rate_Hz(fs_Hz), audio_block_samples(block_size) {} const float sample_rate_Hz; const int audio_block_samples; From fa6885d4ff85f5e5096437efa0e994c82dee5261 Mon Sep 17 00:00:00 2001 From: joerg Date: Sun, 15 May 2022 11:50:27 +0200 Subject: [PATCH 13/15] I2S output was sending 32 bits already, but only 16 were used. Now, send all available precision. Basically, I re-did jcj83429's pull request, which got outdated, to current code: https://github.com/chipaudette/OpenAudio_ArduinoLibrary/pull/8 --- output_i2s_f32.cpp | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/output_i2s_f32.cpp b/output_i2s_f32.cpp index fc87219..d66052d 100644 --- a/output_i2s_f32.cpp +++ b/output_i2s_f32.cpp @@ -164,7 +164,7 @@ uint16_t AudioOutputI2S_F32::block_left_offset = 0; uint16_t AudioOutputI2S_F32::block_right_offset = 0; bool AudioOutputI2S_F32::update_responsibility = false; DMAChannel AudioOutputI2S_F32::dma(false); -DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; +DMAMEM __attribute__((aligned(32))) static uint64_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES]; //DMAMEM static int32_t i2s_tx_buffer[2*AUDIO_BLOCK_SAMPLES]; //2 channels at 32-bits per sample. Local "audio_block_samples" should be no larger than global "AUDIO_BLOCK_SAMPLES" float AudioOutputI2S_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE; @@ -199,18 +199,18 @@ void AudioOutputI2S_F32::begin(bool transferUsing32bit) { CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 dma.TCD->SADDR = i2s_tx_buffer; - dma.TCD->SOFF = 2; - dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); - dma.TCD->NBYTES_MLNO = 2; + dma.TCD->SOFF = 4; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); + dma.TCD->NBYTES_MLNO = 4; //dma.TCD->SLAST = -sizeof(i2s_tx_buffer);//orig from Teensy Audio Library 2020-10-31 dma.TCD->SLAST = -I2S_BUFFER_TO_USE_BYTES; - dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2); + dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 0); dma.TCD->DOFF = 0; //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //orig from Teensy Audio Library 2020-10-31 - dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; dma.TCD->DLASTSGA = 0; //dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;//orig from Teensy Audio Library 2020-10-31 - dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); dma.enable(); //newer location of this line in Teensy Audio library @@ -222,19 +222,19 @@ void AudioOutputI2S_F32::begin(bool transferUsing32bit) { CORE_PIN7_CONFIG = 3; //1:TX_DATA0 dma.TCD->SADDR = i2s_tx_buffer; - dma.TCD->SOFF = 2; - dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); - dma.TCD->NBYTES_MLNO = 2; + dma.TCD->SOFF = 4; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); + dma.TCD->NBYTES_MLNO = 4; //dma.TCD->SLAST = -sizeof(i2s_tx_buffer);//orig from Teensy Audio Library 2020-10-31 dma.TCD->SLAST = -I2S_BUFFER_TO_USE_BYTES; dma.TCD->DOFF = 0; //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //orig from Teensy Audio Library 2020-10-31 - dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; dma.TCD->DLASTSGA = 0; //dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;//orig from Teensy Audio Library 2020-10-31 - dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; - dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2); + dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 0); dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX); dma.enable(); //newer location of this line in Teensy Audio library @@ -253,7 +253,7 @@ void AudioOutputI2S_F32::begin(bool transferUsing32bit) { void AudioOutputI2S_F32::isr(void) { #if defined(KINETISK) || defined(__IMXRT1062__) - int16_t *dest; + int32_t *dest; audio_block_f32_t *blockL, *blockR; uint32_t saddr, offsetL, offsetR; @@ -264,12 +264,12 @@ void AudioOutputI2S_F32::isr(void) // DMA is transmitting the first half of the buffer // so we must fill the second half //dest = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio - dest = (int16_t *)&i2s_tx_buffer[audio_block_samples/2]; //this will be diff if we were to do 32-bit samples + dest = (int32_t *)&i2s_tx_buffer[audio_block_samples/2]; //this will be diff if we were to do 32-bit samples if (AudioOutputI2S_F32::update_responsibility) AudioStream_F32::update_all(); } else { // DMA is transmitting the second half of the buffer // so we must fill the first half - dest = (int16_t *)i2s_tx_buffer; + dest = (int32_t *)i2s_tx_buffer; } blockL = AudioOutputI2S_F32::block_left_1st; @@ -277,15 +277,15 @@ void AudioOutputI2S_F32::isr(void) offsetL = AudioOutputI2S_F32::block_left_offset; offsetR = AudioOutputI2S_F32::block_right_offset; - int16_t *d = dest; + int32_t *d = dest; if (blockL && blockR) { //memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR); //memcpy_tointerleaveLRwLen(dest, blockL->data + offsetL, blockR->data + offsetR, audio_block_samples/2); float32_t *pL = blockL->data + offsetL; float32_t *pR = blockR->data + offsetR; for (int i=0; i < audio_block_samples/2; i++) { - *d++ = (int16_t) *pL++; - *d++ = (int16_t) *pR++; //interleave + *d++ = (int32_t) *pL++; + *d++ = (int32_t) *pR++; //interleave //*d++ = 0; //*d++ = 0; } @@ -294,15 +294,15 @@ void AudioOutputI2S_F32::isr(void) } else if (blockL) { //memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR); float32_t *pL = blockL->data + offsetL; - for (int i=0; i < audio_block_samples / 2 * 2; i+=2) { *(d+i) = (int16_t) *pL++; } //interleave + for (int i=0; i < audio_block_samples / 2 * 2; i+=2) { *(d+i) = (int32_t) *pL++; } //interleave offsetL += audio_block_samples / 2; } else if (blockR) { float32_t *pR = blockR->data + offsetR; - for (int i=0; i < audio_block_samples /2 * 2; i+=2) { *(d+i) = (int16_t) *pR++; } //interleave + for (int i=0; i < audio_block_samples /2 * 2; i+=2) { *(d+i) = (int32_t) *pR++; } //interleave offsetR += audio_block_samples / 2; } else { //memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); - memset(dest,0,audio_block_samples * 2); + memset(dest,0,audio_block_samples * 4); return; } @@ -664,8 +664,8 @@ void AudioOutputI2S_F32::update(void) //scale F32 to Int32 //block_f32_scaled = AudioStream_F32::allocate_f32(); - //scale_f32_to_i32(block_f32->data, block_f32_scaled->data, audio_block_samples); - scale_f32_to_i16(block_f32->data, block_f32_scaled->data, audio_block_samples); + scale_f32_to_i32(block_f32->data, block_f32_scaled->data, audio_block_samples); + //scale_f32_to_i16(block_f32->data, block_f32_scaled->data, audio_block_samples); //count++; //if (count > 100) { @@ -709,8 +709,8 @@ void AudioOutputI2S_F32::update(void) //scale F32 to Int32 //block_f32_scaled = AudioStream_F32::allocate_f32(); - //scale_f32_to_i32(block_f32->data, block_f32_scaled->data, audio_block_samples); - scale_f32_to_i16(block_f32->data, block_f32_scaled->data, audio_block_samples); + scale_f32_to_i32(block_f32->data, block_f32_scaled->data, audio_block_samples); + //scale_f32_to_i16(block_f32->data, block_f32_scaled->data, audio_block_samples); __disable_irq(); if (block_right_1st == NULL) { From 4fe37d5d958830d5e543c317d10719eaf3a9c306 Mon Sep 17 00:00:00 2001 From: joerg Date: Sun, 15 May 2022 11:55:33 +0200 Subject: [PATCH 14/15] Added float modules for 24 bit precision S/PDIF I/O. They are forked from current code of the "offical" (16 bit 44.1kHz only) Teensy audio library, but extended to 24 bit precision and configurable sample rate. --- OpenAudio_ArduinoLibrary.h | 3 + async_input_spdif3_F32.cpp | 431 +++++++++++++++++++++++++++++++++++++ async_input_spdif3_F32.h | 91 ++++++++ input_spdif3_f32.cpp | 260 ++++++++++++++++++++++ input_spdif3_f32.h | 60 ++++++ output_spdif3_f32.cpp | 308 ++++++++++++++++++++++++++ output_spdif3_f32.h | 62 ++++++ 7 files changed, 1215 insertions(+) create mode 100644 async_input_spdif3_F32.cpp create mode 100644 async_input_spdif3_F32.h create mode 100644 input_spdif3_f32.cpp create mode 100644 input_spdif3_f32.h create mode 100644 output_spdif3_f32.cpp create mode 100644 output_spdif3_f32.h diff --git a/OpenAudio_ArduinoLibrary.h b/OpenAudio_ArduinoLibrary.h index 29eceed..55d0393 100644 --- a/OpenAudio_ArduinoLibrary.h +++ b/OpenAudio_ArduinoLibrary.h @@ -22,7 +22,10 @@ #include "AudioMultiply_F32.h" #include "AudioSettings_F32.h" #include "input_i2s_f32.h" +#include "input_spdif3_F32.h" +#include "async_input_spdif3_F32.h" #include "output_i2s_f32.h" +#include "output_spdif3_F32.h" #include "play_queue_f32.h" #include "record_queue_f32.h" #include "synth_pinknoise_f32.h" diff --git a/async_input_spdif3_F32.cpp b/async_input_spdif3_F32.cpp new file mode 100644 index 0000000..3558a76 --- /dev/null +++ b/async_input_spdif3_F32.cpp @@ -0,0 +1,431 @@ +/* Audio Library for Teensy 3.X + * Copyright (c) 2019, Paul Stoffregen, paul@pjrc.com + * + * Development of this audio library was funded by PJRC.COM, LLC by sales of + * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop + * open source software by purchasing Teensy or other PJRC products. + * + * 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. + */ +/* + by Alexander Walch + */ +#if defined(__IMXRT1062__) + +#include "async_input_spdif3_F32.h" +#include "output_spdif3_F32.h" + +#include "biquad.h" +#include +//Parameters +namespace { + #define SPDIF_RX_BUFFER_LENGTH AUDIO_BLOCK_SAMPLES + const int32_t bufferLength=8*AUDIO_BLOCK_SAMPLES; + const uint16_t noSamplerPerIsr=SPDIF_RX_BUFFER_LENGTH/4; + const float toFloatAudio= (float)(1./pow(2., 23.)); +} + +// dummy class, no quantization +class Scaler_F32 { +public: + Scaler_F32() { + _factor = 1.0; + }; + + void configure() { + }; + + void quantize(float* input, float32_t* output, uint16_t length) { + memcpy(output, input, length * sizeof(float)); + /* + for (uint16_t i =0; i< length; i++){ + *output++ = *input++ * _factor; + } + */ + }; +private: + float _factor; +}; + +#ifdef DEBUG_SPDIF_IN +volatile bool AsyncAudioInputSPDIF3_F32::bufferOverflow=false; +#endif + +volatile uint32_t AsyncAudioInputSPDIF3_F32::microsLast; + +DMAMEM __attribute__((aligned(32))) +static int32_t spdif_rx_buffer[SPDIF_RX_BUFFER_LENGTH]; +static float bufferR[bufferLength]; +static float bufferL[bufferLength]; + +volatile int32_t AsyncAudioInputSPDIF3_F32::buffer_offset = 0; // read by resample/ written in spdif input isr -> copied at the beginning of 'resmaple' protected by __disable_irq() in resample +int32_t AsyncAudioInputSPDIF3_F32::resample_offset = 0; // read/written by resample/ read in spdif input isr -> no protection needed? +float AsyncAudioInputSPDIF3_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE_EXACT; + +DMAChannel AsyncAudioInputSPDIF3_F32::dma(false); + +AsyncAudioInputSPDIF3_F32::~AsyncAudioInputSPDIF3_F32(){ + delete [] _bufferLPFilter.pCoeffs; + delete [] _bufferLPFilter.pState; + delete quantizer[0]; + delete quantizer[1]; +} + +FLASHMEM +AsyncAudioInputSPDIF3_F32::AsyncAudioInputSPDIF3_F32(const AudioSettings_F32 &settings, float attenuation, int32_t minHalfFilterLength, int32_t maxHalfFilterLength): + AudioStream_F32(0, NULL), + _resampler(attenuation, minHalfFilterLength, maxHalfFilterLength) + { + sample_rate_Hz = settings.sample_rate_Hz; + quantizer[0]=new Scaler_F32(); + quantizer[0]->configure(); + quantizer[1]=new Scaler_F32(); + quantizer[1]->configure(); + begin(); + } +FLASHMEM +void AsyncAudioInputSPDIF3_F32::begin() +{ + + AudioOutputSPDIF3_F32::config_spdif3(sample_rate_Hz); + + dma.begin(true); // Allocate the DMA channel first + const uint32_t noByteMinorLoop=2*4; + dma.TCD->SOFF = 4; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); + dma.TCD->NBYTES_MLNO = DMA_TCD_NBYTES_MLOFFYES_NBYTES(noByteMinorLoop) | DMA_TCD_NBYTES_SMLOE | + DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8); + dma.TCD->SLAST = -8; + dma.TCD->DOFF = 4; + dma.TCD->CITER_ELINKNO = sizeof(spdif_rx_buffer) / noByteMinorLoop; + dma.TCD->DLASTSGA = -sizeof(spdif_rx_buffer); + dma.TCD->BITER_ELINKNO = sizeof(spdif_rx_buffer) / noByteMinorLoop; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + dma.TCD->SADDR = (void *)((uint32_t)&SPDIF_SRL); + dma.TCD->DADDR = spdif_rx_buffer; + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SPDIF_RX); + + //SPDIF_SCR |=SPDIF_SCR_DMA_RX_EN; //DMA Receive Request Enable + dma.enable(); + dma.attachInterrupt(isr); +#ifdef DEBUG_SPDIF_IN + while (!Serial); +#endif + _bufferLPFilter.pCoeffs=new float[5]; + _bufferLPFilter.numStages=1; + _bufferLPFilter.pState=new float[2]; + getCoefficients(_bufferLPFilter.pCoeffs, BiquadType::LOW_PASS, 0., 5., sample_rate_Hz/AUDIO_BLOCK_SAMPLES, 0.5); + SPDIF_SCR &=(~SPDIF_SCR_RXFIFO_OFF_ON); //receive fifo is turned on again + + SPDIF_SRCD = 0; + SPDIF_SCR |= SPDIF_SCR_DMA_RX_EN; + CORE_PIN15_CONFIG = 3; + IOMUXC_SPDIF_IN_SELECT_INPUT = 0; // GPIO_AD_B1_03_ALT3 +} +bool AsyncAudioInputSPDIF3_F32::isLocked() { + return (SPDIF_SRPC & SPDIF_SRPC_LOCK) == SPDIF_SRPC_LOCK; +} + +void AsyncAudioInputSPDIF3_F32::resample(float32_t* data_left, float32_t* data_right, int32_t& block_offset){ + block_offset=0; + if(!_resampler.initialized() || !isLocked()){ + return; + } + int32_t bOffset=buffer_offset; + int32_t resOffset=resample_offset; + + uint16_t inputBufferStop = bOffset >= resOffset ? bOffset-resOffset : bufferLength-resOffset; + if (inputBufferStop==0){ + return; + } + uint16_t processedLength; + uint16_t outputCount=0; + uint16_t outputLength=AUDIO_BLOCK_SAMPLES; + + float resampledBufferL[AUDIO_BLOCK_SAMPLES]; + float resampledBufferR[AUDIO_BLOCK_SAMPLES]; + _resampler.resample(&bufferL[resOffset],&bufferR[resOffset], inputBufferStop, processedLength, resampledBufferL, resampledBufferR, outputLength, outputCount); + + resOffset=(resOffset+processedLength)%bufferLength; + block_offset=outputCount; + + if (bOffset > resOffset && block_offset< AUDIO_BLOCK_SAMPLES){ + inputBufferStop= bOffset-resOffset; + outputLength=AUDIO_BLOCK_SAMPLES-block_offset; + _resampler.resample(&bufferL[resOffset],&bufferR[resOffset], inputBufferStop, processedLength, resampledBufferL+block_offset, resampledBufferR+block_offset, outputLength, outputCount); + resOffset=(resOffset+processedLength)%bufferLength; + block_offset+=outputCount; + } + quantizer[0]->quantize(resampledBufferL, data_left, block_offset); // TODO: degenerated to a copy, perhaps directly resample into here? + quantizer[1]->quantize(resampledBufferR, data_right, block_offset); + __disable_irq(); + resample_offset=resOffset; + __enable_irq(); +} + +void AsyncAudioInputSPDIF3_F32::isr(void) +{ + dma.clearInterrupt(); + microsLast=micros(); + const int32_t *src, *end; + uint32_t daddr = (uint32_t)(dma.TCD->DADDR); + + if (daddr < (uint32_t)spdif_rx_buffer + sizeof(spdif_rx_buffer) / 2) { + // DMA is receiving to the first half of the buffer + // need to remove data from the second half + src = (int32_t *)&spdif_rx_buffer[SPDIF_RX_BUFFER_LENGTH/2]; + end = (int32_t *)&spdif_rx_buffer[SPDIF_RX_BUFFER_LENGTH]; + //if (AsyncAudioInputSPDIF3_F32::update_responsibility) AudioStream::update_all(); + } else { + // DMA is receiving to the second half of the buffer + // need to remove data from the first half + src = (int32_t *)&spdif_rx_buffer[0]; + end = (int32_t *)&spdif_rx_buffer[SPDIF_RX_BUFFER_LENGTH/2]; + } + if (buffer_offset >=resample_offset || + (buffer_offset + SPDIF_RX_BUFFER_LENGTH/4) < resample_offset) { + #if IMXRT_CACHE_ENABLED >=1 + arm_dcache_delete((void*)src, sizeof(spdif_rx_buffer) / 2); + #endif + float *destR = &(bufferR[buffer_offset]); + float *destL = &(bufferL[buffer_offset]); + do { + int32_t n=(*src) & 0x800000 ? (*src)|0xFF800000 : (*src) & 0xFFFFFF; + *destL++ = (float)(n)*toFloatAudio; + ++src; + + n=(*src) & 0x800000 ? (*src)|0xFF800000 : (*src) & 0xFFFFFF; + *destR++ = (float)(n)*toFloatAudio; + ++src; + } while (src < end); + buffer_offset=(buffer_offset+SPDIF_RX_BUFFER_LENGTH/4)%bufferLength; + } +#ifdef DEBUG_SPDIF_IN + else { + bufferOverflow=true; + } +#endif +} +double AsyncAudioInputSPDIF3_F32::getNewValidInputFrequ(){ + //page 2129: FrequMeas[23:0]=FreqMeas_CLK / BUS_CLK * 2^10 * GAIN + if (isLocked()){ + const double f=(double)F_BUS_ACTUAL/(1048576.*(double)AudioOutputSPDIF3_F32::dpll_Gain()*128.);// bit clock = 128 * sampling frequency + const double freqMeas=(double)(SPDIF_SRFM & 0xFFFFFF)*f; + if (_lastValidInputFrequ != freqMeas){//frequency not stable yet; + _lastValidInputFrequ=freqMeas; + return -1.; + } + return _lastValidInputFrequ; + } + return -1.; +} + +double AsyncAudioInputSPDIF3_F32::getBufferedTime() const{ + __disable_irq(); + double n=_bufferedTime; + __enable_irq(); + return n; +} + +void AsyncAudioInputSPDIF3_F32::configure(){ + if(!isLocked()){ + _resampler.reset(); + return; + } + +#ifdef DEBUG_SPDIF_IN + const bool bOverf=bufferOverflow; + bufferOverflow=false; + if (bOverf){ + Serial.print("buffer overflow, buffer offset: "); + Serial.print(buffer_offset); + Serial.print(", resample_offset: "); + Serial.println(resample_offset); + if (!_resampler.initialized()){ + Serial.println("_resampler not initialized. "); + } + } +#endif + const double inputF=getNewValidInputFrequ(); //returns: -1 ... invalid frequency + if (inputF > 0.){ + //we got a valid sample frequency + const double frequDiff=inputF/_inputFrequency-1.; + if (abs(frequDiff) > 0.01 || !_resampler.initialized()){ + //the new sample frequency differs from the last one -> configure the _resampler again + _inputFrequency=inputF; + _targetLatencyS=max(0.001,(noSamplerPerIsr*3./2./_inputFrequency)); + _maxLatency=max(2.*_blockDuration, 2*noSamplerPerIsr/_inputFrequency); + const int32_t targetLatency=round(_targetLatencyS*inputF); + __disable_irq(); + resample_offset = targetLatency <= buffer_offset ? buffer_offset - targetLatency : bufferLength -(targetLatency-buffer_offset); + __enable_irq(); + _resampler.configure(inputF, sample_rate_Hz); + #ifdef DEBUG_SPDIF_IN + Serial.print("_maxLatency: "); + Serial.println(_maxLatency); + Serial.print("targetLatency: "); + Serial.println(targetLatency); + Serial.print("relative frequ diff: "); + Serial.println(frequDiff, 8); + Serial.print("configure _resampler with frequency "); + Serial.println(inputF,8); + #endif + } + } +} + +void AsyncAudioInputSPDIF3_F32::monitorResampleBuffer(){ + if(!_resampler.initialized()){ + return; + } + __disable_irq(); + const double dmaOffset=(micros()-microsLast)*1e-6; //[seconds] + double bTime = resample_offset <= buffer_offset ? (buffer_offset-resample_offset-_resampler.getXPos())/_lastValidInputFrequ+dmaOffset : (bufferLength-resample_offset +buffer_offset-_resampler.getXPos())/_lastValidInputFrequ+dmaOffset; //[seconds] + + double diff = bTime- (_blockDuration+ _targetLatencyS); //seconds + + biquad_cascade_df2T(&_bufferLPFilter, &diff, &diff, 1); + + bool settled=_resampler.addToSampleDiff(diff); + + if (bTime > _maxLatency || bTime-dmaOffset<= _blockDuration || settled) { + double distance=(_blockDuration+_targetLatencyS-dmaOffset)*_lastValidInputFrequ+_resampler.getXPos(); + diff=0.; + if (distance > bufferLength-noSamplerPerIsr){ + diff=bufferLength-noSamplerPerIsr-distance; + distance=bufferLength-noSamplerPerIsr; + } + if (distance < 0.){ + distance=0.; + diff=- (_blockDuration+ _targetLatencyS); + } + double resample_offsetF=buffer_offset-distance; + resample_offset=(int32_t)floor(resample_offsetF); + _resampler.addToPos(resample_offsetF-resample_offset); + while (resample_offset<0){ + resample_offset+=bufferLength; + } +#ifdef DEBUG_SPDIF_IN + double bTimeFixed = resample_offset <= buffer_offset ? (buffer_offset-resample_offset-_resampler.getXPos())/_lastValidInputFrequ+dmaOffset : (bufferLength-resample_offset +buffer_offset-_resampler.getXPos())/_lastValidInputFrequ+dmaOffset; //[seconds] +#endif + __enable_irq(); +#ifdef DEBUG_SPDIF_IN + Serial.print("settled: "); + Serial.println(settled); + Serial.print("bTime: "); + Serial.println(bTime*1e6,3); + Serial.print("_maxLatency: "); + Serial.println(_maxLatency*1e6,3); + Serial.print("bTime-dmaOffset: "); + Serial.println((bTime-dmaOffset)*1e6,3); + Serial.print(", _blockDuration: "); + Serial.println(_blockDuration*1e6,3); + Serial.print("bTimeFixed: "); + Serial.println(bTimeFixed*1e6,3); + +#endif + preload(&_bufferLPFilter, (float)diff); + _resampler.fixStep(); + } + else { + __enable_irq(); + } + _bufferedTime=_targetLatencyS+diff; +} + +void AsyncAudioInputSPDIF3_F32::update(void) +{ + configure(); + monitorResampleBuffer(); //important first call 'monitorResampleBuffer' then 'resample' + audio_block_f32_t *block_left = allocate_f32(); + audio_block_f32_t *block_right = nullptr; + if (block_left!= nullptr) { + block_right = allocate_f32(); + if (block_right == nullptr) { + release(block_left); + block_left = nullptr; + } + } + if (block_left && block_right) { + int32_t block_offset; + resample(block_left->data, block_right->data,block_offset); + if(block_offset < AUDIO_BLOCK_SAMPLES){ + memset(block_left->data+block_offset, 0, (AUDIO_BLOCK_SAMPLES-block_offset)*sizeof(float32_t)); + memset(block_right->data+block_offset, 0, (AUDIO_BLOCK_SAMPLES-block_offset)*sizeof(float32_t)); +#ifdef DEBUG_SPDIF_IN + Serial.print("filled only "); + Serial.print(block_offset); + Serial.println(" samples."); +#endif + } + transmit(block_left, 0); + release(block_left); + block_left=nullptr; + transmit(block_right, 1); + release(block_right); + block_right=nullptr; + } +#ifdef DEBUG_SPDIF_IN + else { + Serial.println("Not enough blocks available. Too few audio memory?"); + } +#endif +} +double AsyncAudioInputSPDIF3_F32::getInputFrequency() const{ + __disable_irq(); + double f=_lastValidInputFrequ; + __enable_irq(); + return isLocked() ? f : 0.; +} +double AsyncAudioInputSPDIF3_F32::getTargetLantency() const { + __disable_irq(); + double l=_targetLatencyS; + __enable_irq(); + return l ; +} +double AsyncAudioInputSPDIF3_F32::getAttenuation() const{ + return _resampler.getAttenuation(); +} +int32_t AsyncAudioInputSPDIF3_F32::getHalfFilterLength() const{ + return _resampler.getHalfFilterLength(); +} + +#endif // __IMXRT1062__ + + +#if defined(__MK66FX1M0__) || defined(__MK64FX512__) || defined(__MK20DX256__) || defined(__MKL26Z64__) +// empty code to allow compile (but no sound input) on other Teensy models + +#include "async_input_spdif3.h" +AsyncAudioInputSPDIF3_F32::AsyncAudioInputSPDIF3_F32(bool dither, bool noiseshaping,float attenuation, int32_t minHalfFilterLength, int32_t maxHalfFilterLength): + AudioStream(0, NULL), _resampler(attenuation, minHalfFilterLength, maxHalfFilterLength) + { } +void AsyncAudioInputSPDIF3_F32::begin() { } +void AsyncAudioInputSPDIF3_F32::update(void) { } +double AsyncAudioInputSPDIF3_F32::getBufferedTime() const { return 0; } +double AsyncAudioInputSPDIF3_F32::getInputFrequency() const { return 0; } +bool AsyncAudioInputSPDIF3_F32::isLocked() { return false; } +double AsyncAudioInputSPDIF3_F32::getTargetLantency() const { return 0; } +double AsyncAudioInputSPDIF3_F32::getAttenuation() const { return 0; } +int32_t AsyncAudioInputSPDIF3_F32::getHalfFilterLength() const { return 0; } +AsyncAudioInputSPDIF3_F32::~AsyncAudioInputSPDIF3_F32() { } + + +#endif + diff --git a/async_input_spdif3_F32.h b/async_input_spdif3_F32.h new file mode 100644 index 0000000..b808a9a --- /dev/null +++ b/async_input_spdif3_F32.h @@ -0,0 +1,91 @@ +/* Audio Library for Teensy 3.X + * Copyright (c) 2019, Paul Stoffregen, paul@pjrc.com + * + * Development of this audio library was funded by PJRC.COM, LLC by sales of + * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop + * open source software by purchasing Teensy or other PJRC products. + * + * 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. + */ +/* + by Alexander Walch + */ +#ifndef async_input_spdif3_f32_h_ +#define async_input_spdif3_f32_h_ +#include "Resampler.h" +#include "Quantizer.h" +#include "Arduino.h" +#include "AudioStream_F32.h" +#include "DMAChannel.h" +#include + +//#define DEBUG_SPDIF_IN //activates debug output + +class Scaler_F32; // internal + +class AsyncAudioInputSPDIF3_F32 : public AudioStream_F32 +{ +public: + ///@param attenuation target attenuation [dB] of the anti-aliasing filter. Only used if AUDIO_SAMPLE_RATE_EXACT < input sample rate (input fs). The attenuation can't be reached if the needed filter length exceeds 2*MAX_FILTER_SAMPLES+1 + ///@param minHalfFilterLength If AUDIO_SAMPLE_RATE_EXACT >= input fs), the filter length of the resampling filter is 2*minHalfFilterLength+1. If AUDIO_SAMPLE_RATE_EXACT < input fs the filter is maybe longer to reach the desired attenuation + ///@param maxHalfFilterLength Can be used to restrict the maximum filter length at the cost of a lower attenuation + AsyncAudioInputSPDIF3_F32(const AudioSettings_F32 &settings, float attenuation=100, int32_t minHalfFilterLength=20, int32_t maxHalfFilterLength=80); + ~AsyncAudioInputSPDIF3_F32(); + void begin(); + virtual void update(void); + double getBufferedTime() const; + double getInputFrequency() const; + static bool isLocked(); + double getTargetLantency() const; + double getAttenuation() const; + int32_t getHalfFilterLength() const; +protected: + static DMAChannel dma; + static void isr(void); +private: + void resample(float32_t* data_left, float32_t* data_right, int32_t& block_offset); + void monitorResampleBuffer(); + void configure(); + double getNewValidInputFrequ(); + void config_spdifIn(); + + //accessed in isr ==== + static volatile int32_t buffer_offset; + static int32_t resample_offset; + static volatile uint32_t microsLast; + //==================== + + Resampler _resampler; + Scaler_F32* quantizer[2]; + arm_biquad_cascade_df2T_instance_f32 _bufferLPFilter; + + volatile double _bufferedTime; + volatile double _lastValidInputFrequ; + double _inputFrequency=0.; + double _targetLatencyS; //target latency [seconds] + const double _blockDuration=AUDIO_BLOCK_SAMPLES/AUDIO_SAMPLE_RATE_EXACT; //[seconds] + double _maxLatency=2.*_blockDuration; + static float sample_rate_Hz; // configured output sample rate + +#ifdef DEBUG_SPDIF_IN + static volatile bool bufferOverflow; +#endif +}; + +#endif \ No newline at end of file diff --git a/input_spdif3_f32.cpp b/input_spdif3_f32.cpp new file mode 100644 index 0000000..b516ab2 --- /dev/null +++ b/input_spdif3_f32.cpp @@ -0,0 +1,260 @@ +/* Audio Library for Teensy 3.X + * Copyright (c) 2019, Paul Stoffregen, paul@pjrc.com + * + * Development of this audio library was funded by PJRC.COM, LLC by sales of + * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop + * open source software by purchasing Teensy or other PJRC products. + * + * 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. + */ +/* + by Frank Bösing + */ + +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) +#include +#include "input_spdif3_F32.h" +#include "output_spdif3_F32.h" +#include "utility/imxrt_hw.h" + +// sign extend and scale +static inline float32_t i24_to_f32(int32_t n) { + const float32_t scale = 1.0 / (1LL << 31); + int32_t leftaligned = (uint32_t)n << 8; // to avoid manual sign extension + return scale * leftaligned; +} + +DMAMEM __attribute__((aligned(32))) +static uint32_t spdif_rx_buffer[AUDIO_BLOCK_SAMPLES * 4]; +audio_block_f32_t * AudioInputSPDIF3_F32::block_left = NULL; +audio_block_f32_t * AudioInputSPDIF3_F32::block_right = NULL; +uint16_t AudioInputSPDIF3_F32::block_offset = 0; +bool AudioInputSPDIF3_F32::update_responsibility = false; +DMAChannel AudioInputSPDIF3_F32::dma(false); + +FLASHMEM +void AudioInputSPDIF3_F32::begin(void) +{ + dma.begin(true); // Allocate the DMA channel first + + AudioOutputSPDIF3_F32::config_spdif3(sample_rate_Hz); + + const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop + dma.TCD->SADDR = &SPDIF_SRL; + dma.TCD->SOFF = 4; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); + dma.TCD->NBYTES_MLNO = DMA_TCD_NBYTES_MLOFFYES_NBYTES(nbytes_mlno) | DMA_TCD_NBYTES_SMLOE | + DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8); + dma.TCD->SLAST = -8; + dma.TCD->DADDR = spdif_rx_buffer; + dma.TCD->DOFF = 4; + dma.TCD->DLASTSGA = -sizeof(spdif_rx_buffer); + dma.TCD->CITER_ELINKNO = sizeof(spdif_rx_buffer) / nbytes_mlno; + dma.TCD->BITER_ELINKNO = sizeof(spdif_rx_buffer) / nbytes_mlno; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SPDIF_RX); + update_responsibility = update_setup(); + dma.attachInterrupt(isr); + dma.enable(); + + SPDIF_SRCD = 0; + SPDIF_SCR |= SPDIF_SCR_DMA_RX_EN; + CORE_PIN15_CONFIG = 3; + IOMUXC_SPDIF_IN_SELECT_INPUT = 0; // GPIO_AD_B1_03_ALT3 + //pinMode(13, OUTPUT); +} + +void AudioInputSPDIF3_F32::isr(void) +{ + uint32_t daddr, offset; + const int32_t *src, *end; + float32_t *dest_left, *dest_right; + audio_block_f32_t *left, *right; + + dma.clearInterrupt(); + //digitalWriteFast(13, !digitalReadFast(13)); + if (AudioInputSPDIF3_F32::update_responsibility) AudioStream::update_all(); + + daddr = (uint32_t)(dma.TCD->DADDR); + + if (daddr < (uint32_t)spdif_rx_buffer + sizeof(spdif_rx_buffer) / 2) { + // DMA is receiving to the first half of the buffer + // need to remove data from the second half + src = (int32_t *)&spdif_rx_buffer[AUDIO_BLOCK_SAMPLES * 2]; + end = (int32_t *)&spdif_rx_buffer[AUDIO_BLOCK_SAMPLES * 4]; + } else { + // DMA is receiving to the second half of the buffer + // need to remove data from the first half + src = (int32_t *)&spdif_rx_buffer[0]; + end = (int32_t *)&spdif_rx_buffer[AUDIO_BLOCK_SAMPLES*2]; + } + + left = AudioInputSPDIF3_F32::block_left; + right = AudioInputSPDIF3_F32::block_right; + + if (left != NULL && right != NULL) { + offset = AudioInputSPDIF3_F32::block_offset; + if (offset <= AUDIO_BLOCK_SAMPLES*2) { + dest_left = &(left->data[offset]); + dest_right = &(right->data[offset]); + AudioInputSPDIF3_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES*2; + + do { + #if IMXRT_CACHE_ENABLED >=1 + SCB_CACHE_DCIMVAC = (uintptr_t)src; + asm("dsb":::"memory"); + #endif + + *dest_left++ = i24_to_f32(*src++); + *dest_right++ = i24_to_f32(*src++); + + *dest_left++ = i24_to_f32(*src++); + *dest_right++ = i24_to_f32(*src++); + + *dest_left++ = i24_to_f32(*src++); + *dest_right++ = i24_to_f32(*src++); + + *dest_left++ = i24_to_f32(*src++); + *dest_right++ = i24_to_f32(*src++); + + } while (src < end); + } + } + else if (left != NULL) { + offset = AudioInputSPDIF3_F32::block_offset; + if (offset <= AUDIO_BLOCK_SAMPLES*2) { + dest_left = &(left->data[offset]); + AudioInputSPDIF3_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES*2; + + do { + #if IMXRT_CACHE_ENABLED >=1 + SCB_CACHE_DCIMVAC = (uintptr_t)src; + asm("dsb":::"memory"); + #endif + + *dest_left++ = i24_to_f32(*src++); + src++; + + *dest_left++ = i24_to_f32(*src++); + src++; + + *dest_left++ = i24_to_f32(*src++); + src++; + + *dest_left++ = i24_to_f32(*src++); + src++; + + } while (src < end); + } + } + else if (right != NULL) { + offset = AudioInputSPDIF3_F32::block_offset; + if (offset <= AUDIO_BLOCK_SAMPLES*2) { + dest_right = &(right->data[offset]); + AudioInputSPDIF3_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES*2; + + do { + #if IMXRT_CACHE_ENABLED >=1 + SCB_CACHE_DCIMVAC = (uintptr_t)src; + asm("dsb":::"memory"); + #endif + + src++; + *dest_right++ = i24_to_f32(*src++); + + src++; + *dest_right++ = i24_to_f32(*src++); + + src++; + *dest_right++ = i24_to_f32(*src++); + + src++; + *dest_right++ = i24_to_f32(*src++); + + } while (src < end); + } + } + +} + + +void AudioInputSPDIF3_F32::update(void) +{ + audio_block_f32_t *new_left=NULL, *new_right=NULL, *out_left=NULL, *out_right=NULL; + + // allocate 2 new blocks, but if one fails, allocate neither + new_left = allocate_f32(); + if (new_left != NULL) { + new_right = allocate_f32(); + if (new_right == NULL) { + release(new_left); + new_left = NULL; + } + } + __disable_irq(); + if (block_offset >= AUDIO_BLOCK_SAMPLES) { + // the DMA filled 2 blocks, so grab them and get the + // 2 new blocks to the DMA, as quickly as possible + out_left = block_left; + block_left = new_left; + out_right = block_right; + block_right = new_right; + block_offset = 0; + __enable_irq(); + // then transmit the DMA's former blocks + transmit(out_left, 0); + release(out_left); + transmit(out_right, 1); + release(out_right); + //Serial.print("."); + } else if (new_left != NULL) { + // the DMA didn't fill blocks, but we allocated blocks + if (block_left == NULL) { + // the DMA doesn't have any blocks to fill, so + // give it the ones we just allocated + block_left = new_left; + block_right = new_right; + block_offset = 0; + __enable_irq(); + } else { + // the DMA already has blocks, doesn't need these + __enable_irq(); + release(new_left); + release(new_right); + } + } else { + // The DMA didn't fill blocks, and we could not allocate + // memory... the system is likely starving for memory! + // Sadly, there's nothing we can do. + __enable_irq(); + } +} + +bool AudioInputSPDIF3_F32::pllLocked(void) +{ + return (SPDIF_SRPC & SPDIF_SRPC_LOCK) == SPDIF_SRPC_LOCK ? true:false; +} + +unsigned int AudioInputSPDIF3_F32::sampleRate(void) { + if (!pllLocked()) return 0; + return (float)((uint64_t)F_BUS_ACTUAL * SPDIF_SRFM) / (0x8000000ULL * AudioOutputSPDIF3_F32::dpll_Gain()) + 0.5F; +} + +#endif diff --git a/input_spdif3_f32.h b/input_spdif3_f32.h new file mode 100644 index 0000000..ee1492b --- /dev/null +++ b/input_spdif3_f32.h @@ -0,0 +1,60 @@ +/* Audio Library for Teensy 3.X + * Copyright (c) 2019, Paul Stoffregen, paul@pjrc.com + * + * Development of this audio library was funded by PJRC.COM, LLC by sales of + * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop + * open source software by purchasing Teensy or other PJRC products. + * + * 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. + */ +// Frank Bösing + +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) +#ifndef _input_spdif3_f32_h_ +#define _input_spdif3_f32_h_ + +#include "Arduino.h" +#include "AudioStream_F32.h" +#include "DMAChannel.h" + +class AudioInputSPDIF3_F32 : public AudioStream_F32 +{ +public: + AudioInputSPDIF3_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL) { + sample_rate_Hz = settings.sample_rate_Hz; + begin(); + } + virtual void update(void); + void begin(void); + static bool pllLocked(void); + static unsigned int sampleRate(void); +protected: + //AudioInputSPDIF3_F32(int dummy): AudioStream_F32(0, NULL) {} // to be used only inside AudioInputSPDIF3slave !! + static bool update_responsibility; + static DMAChannel dma; + static void isr(void); +private: + static audio_block_f32_t *block_left; + static audio_block_f32_t *block_right; + static uint16_t block_offset; + static float sample_rate_Hz; +}; + +#endif +#endif diff --git a/output_spdif3_f32.cpp b/output_spdif3_f32.cpp new file mode 100644 index 0000000..76bf3dc --- /dev/null +++ b/output_spdif3_f32.cpp @@ -0,0 +1,308 @@ +/* Hardware-SPDIF for Teensy 4 + * Copyright (c) 2019, Frank Bösing, f.boesing@gmx.de + * + * 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. + */ +/* + http://www.hardwarebook.info/S/PDIF + https://www.mikrocontroller.net/articles/S/PDIF + https://en.wikipedia.org/wiki/S/PDIF +*/ +#include +#include "output_spdif3_f32.h" + +#if defined(__IMXRT1062__) + +#include "utility/imxrt_hw.h" +#include "memcpy_audio.h" +#include + +// TODO: convert within update() instead of isr(), into buffer +static inline int32_t f32_to_i24(float32_t f) { + const float32_t fullscale = (1LL << 23) - 1; + if (f > 1.0) return fullscale; + if (f < -1.0) return -fullscale; + return (int32_t)(f * fullscale); +} + +audio_block_f32_t * AudioOutputSPDIF3_F32::block_left_1st = nullptr; +audio_block_f32_t * AudioOutputSPDIF3_F32::block_right_1st = nullptr; +audio_block_f32_t * AudioOutputSPDIF3_F32::block_left_2nd = nullptr; +audio_block_f32_t * AudioOutputSPDIF3_F32::block_right_2nd = nullptr; +bool AudioOutputSPDIF3_F32::update_responsibility = false; +float AudioOutputSPDIF3_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE_EXACT; +DMAChannel AudioOutputSPDIF3_F32::dma(false); + +DMAMEM __attribute__((aligned(32))) +static int32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; +DMAMEM __attribute__((aligned(32))) +audio_block_f32_t AudioOutputSPDIF3_F32::block_silent; + +#define SPDIF_DPLL_GAIN24 0 +#define SPDIF_DPLL_GAIN16 1 +#define SPDIF_DPLL_GAIN12 2 +#define SPDIF_DPLL_GAIN8 3 +#define SPDIF_DPLL_GAIN6 4 +#define SPDIF_DPLL_GAIN4 5 +#define SPDIF_DPLL_GAIN3 6 +#define SPDIF_DPLL_GAIN1 7 + +#define SPDIF_DPLL_GAIN SPDIF_DPLL_GAIN8 //Actual Gain +static const uint8_t spdif_gain[8] = {24, 16, 12, 8, 6, 4, 3, 1}; + +FLASHMEM +void AudioOutputSPDIF3_F32::begin(void) +{ + + dma.begin(true); // Allocate the DMA channel first + + block_left_1st = nullptr; + block_right_1st = nullptr; + memset(&block_silent, 0, sizeof(block_silent)); + + config_spdif3(sample_rate_Hz); + const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop + + dma.TCD->SADDR = SPDIF_tx_buffer; + dma.TCD->SOFF = 4; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); + dma.TCD->NBYTES_MLNO = DMA_TCD_NBYTES_MLOFFYES_NBYTES(nbytes_mlno) | DMA_TCD_NBYTES_DMLOE | + DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8); + dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer); + dma.TCD->DADDR = &SPDIF_STL; + dma.TCD->DOFF = 4; + dma.TCD->DLASTSGA = -8; + //dma.TCD->ATTR_DST = ((31 - __builtin_clz(8)) << 3); + dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno; + dma.TCD->BITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SPDIF_TX); + + update_responsibility = update_setup(); + dma.enable(); + dma.attachInterrupt(isr); + + CORE_PIN14_CONFIG = 3; //3:SPDIF_OUT + SPDIF_SCR |= SPDIF_SCR_DMA_TX_EN; + SPDIF_STC |= SPDIF_STC_TX_ALL_CLK_EN; +// pinMode(13, OUTPUT); +} + +void AudioOutputSPDIF3_F32::isr(void) +{ + + const float32_t *src_left, *src_right; + const int32_t *end; + int32_t *dest; + audio_block_f32_t *block_left, *block_right; + uint32_t saddr; + + saddr = (uint32_t)(dma.TCD->SADDR); + dma.clearInterrupt(); + if (saddr < (uint32_t)SPDIF_tx_buffer + sizeof(SPDIF_tx_buffer) / 2) { + // DMA is transmitting the first half of the buffer + // so we must fill the second half + dest = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*2; + end = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*4; + } else { + // DMA is transmitting the second half of the buffer + // so we must fill the first half + dest = SPDIF_tx_buffer; + end = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*2; + } + block_left = block_left_1st; + if (!block_left) block_left = &block_silent; + block_right = block_right_1st; + if (!block_right) block_right = &block_silent; + + src_left = (const float32_t *)(block_left->data); + src_right = (const float32_t *)(block_right->data); + + do { + #if IMXRT_CACHE_ENABLED >= 2 + SCB_CACHE_DCCIMVAC = (uintptr_t) dest; + asm volatile("dsb"); + #endif + + *dest++ = f32_to_i24(*src_left++); + *dest++ = f32_to_i24(*src_right++); + + *dest++ = f32_to_i24(*src_left++); + *dest++ = f32_to_i24(*src_right++); + + *dest++ = f32_to_i24(*src_left++); + *dest++ = f32_to_i24(*src_right++); + + *dest++ = f32_to_i24(*src_left++); + *dest++ = f32_to_i24(*src_right++); + + } while (dest < end); + + if (block_left != &block_silent) { + release(block_left); + block_left_1st = block_left_2nd; + block_left_2nd = nullptr; + } + if (block_right != &block_silent) { + release(block_right); + block_right_1st = block_right_2nd; + block_right_2nd = nullptr; + } + + if (update_responsibility) update_all(); + //digitalWriteFast(13,!digitalReadFast(13)); +} + +void AudioOutputSPDIF3_F32::update(void) +{ + audio_block_f32_t *block_left, *block_right; + + block_left = receiveReadOnly_f32(0); // input 0 + block_right = receiveReadOnly_f32(1); // input 1 + __disable_irq(); + if (block_left) { + if (block_left_1st == nullptr) { + block_left_1st = block_left; + block_left = nullptr; + } else if (block_left_2nd == nullptr) { + block_left_2nd = block_left; + block_left = nullptr; + } else { + audio_block_f32_t *tmp = block_left_1st; + block_left_1st = block_left_2nd; + block_left_2nd = block_left; + block_left = tmp; + } + } + if (block_right) { + if (block_right_1st == nullptr) { + block_right_1st = block_right; + block_right = nullptr; + } else if (block_right_2nd == nullptr) { + block_right_2nd = block_right; + block_right = nullptr; + } else { + audio_block_f32_t *tmp = block_right_1st; + block_right_1st = block_right_2nd; + block_right_2nd = block_right; + block_right = tmp; + } + } + __enable_irq(); + if (block_left) { + release(block_left); + } + if (block_right) { + release(block_right); + } + +} + +void AudioOutputSPDIF3_F32::mute_PCM(const bool mute) +{ + if (mute) + SPDIF_SCR |= SPDIF_SCR_VALCTRL; + else + SPDIF_SCR &= ~SPDIF_SCR_VALCTRL; +} + +uint32_t AudioOutputSPDIF3_F32::dpll_Gain(void) +{ + return spdif_gain[SPDIF_DPLL_GAIN]; +} + +FLASHMEM +void AudioOutputSPDIF3_F32::config_spdif3(float fs_Hz) +{ + delay(1); //WHY IS THIS NEEDED? + + uint32_t fs = fs_Hz; + // PLL between 27*24 = 648MHz und 54*24=1296MHz + // n1, n2 choosen for compatibility with I2S (same PLL frequency) : + 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); + + //use new pred/podf values + n1 = 7; //0: divide by 1 (do not use with high input frequencies), 1:/2, 2: /3, 7:/8 + n2 = 0; //0: divide by 1, 7: divide by 8 + + CCM_CCGR5 &= ~CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate off + + CCM_CDCDR = (CCM_CDCDR & ~(CCM_CDCDR_SPDIF0_CLK_SEL_MASK | CCM_CDCDR_SPDIF0_CLK_PRED_MASK | CCM_CDCDR_SPDIF0_CLK_PODF_MASK)) + | CCM_CDCDR_SPDIF0_CLK_SEL(0) // 0 PLL4, 1 PLL3 PFD2, 2 PLL5, 3 pll3_sw_clk + | CCM_CDCDR_SPDIF0_CLK_PRED(n1) + | CCM_CDCDR_SPDIF0_CLK_PODF(n2); + + CCM_CCGR5 |= CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate on + + if (!(SPDIF_SCR & (SPDIF_SCR_DMA_RX_EN | SPDIF_SCR_DMA_TX_EN))) { + //Serial.print("Reset SPDIF3"); + SPDIF_SCR = SPDIF_SCR_SOFT_RESET; //Reset SPDIF + while (SPDIF_SCR & SPDIF_SCR_SOFT_RESET) {;} //Wait for Reset (takes 8 cycles) + } else return; + + SPDIF_SCR = + SPDIF_SCR_RXFIFOFULL_SEL(0) | // Full interrupt if at least 1 sample in Rx left and right FIFOs + SPDIF_SCR_RXAUTOSYNC | + SPDIF_SCR_TXAUTOSYNC | + SPDIF_SCR_TXFIFOEMPTY_SEL(2) | // Empty interrupt if at most 8 samples in Tx left and right FIFOs + SPDIF_SCR_TXFIFO_CTRL(1) | // 0:Send zeros 1: normal operation + SPDIF_SCR_VALCTRL | // Outgoing Validity always clear + SPDIF_SCR_TXSEL(5) | // 0:off and output 0, 1:Feed-though SPDIFIN, 5:Tx Normal operation + SPDIF_SCR_USRC_SEL(3); + + SPDIF_SRPC = + SPDIF_SRPC_CLKSRC_SEL(1) | //if (DPLL Locked) SPDIF_RxClk else tx_clk (SPDIF0_CLK_ROOT) + SPDIF_SRPC_GAINSEL(SPDIF_DPLL_GAIN); + + uint32_t pllclock = (c0 + (float)c1 / c2) * 24000000ULL; //677376000 Hz + uint32_t clock = pllclock / (1 + n1) / (1 + n2); + uint32_t clkdiv = clock / (fs * 64); // 1 .. 128 + uint32_t mod = clock % (fs * 64); + if (mod > ((fs * 64) / 2)) clkdiv += 1; //nearest divider + +#if 0 + Serial.printf("PLL: %d\n", pllclock); + Serial.printf("clock: %d\n", clock); + Serial.printf("clkdiv: %d\n", clkdiv); +#endif + + SPDIF_STC = + SPDIF_STC_TXCLK_SOURCE(1) | //tx_clk input (from SPDIF0_CLK_ROOT) + SPDIF_STC_TXCLK_DF(clkdiv - 1); +} + +#endif // __IMXRT1062__ + + +#if defined(__MK66FX1M0__) || defined(__MK64FX512__) || defined(__MK20DX256__) || defined(__MKL26Z64__) +// empty code to allow compile (but no sound output) on other Teensy models + +void AudioOutputSPDIF3_F32::update(void) { } +void AudioOutputSPDIF3_F32::begin(void) { } +void AudioOutputSPDIF3_F32::mute_PCM(const bool mute) { } +bool AudioOutputSPDIF3_F32::pll_locked(void) { return false; } + +#endif diff --git a/output_spdif3_f32.h b/output_spdif3_f32.h new file mode 100644 index 0000000..7821633 --- /dev/null +++ b/output_spdif3_f32.h @@ -0,0 +1,62 @@ +/* Hardware-SPDIF for Teensy 4 + * Copyright (c) 2019, Frank Bösing, f.boesing@gmx.de + * + * 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_SPDIF3_f32_h_ +#define output_SPDIF3_f32_h_ + +#include +#include "AudioStream_F32.h" +//include "AudioStream.h" +#include + +class AudioOutputSPDIF3_F32 : public AudioStream_F32 +{ +public: + AudioOutputSPDIF3_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray) { + sample_rate_Hz = settings.sample_rate_Hz; + begin(); + } + virtual void update(void); + void begin(void); + friend class AudioInputSPDIF3_F32; + friend class AsyncAudioInputSPDIF3_F32; + static void mute_PCM(const bool mute); + static bool pll_locked(void); +protected: + //AudioOutputSPDIF3_F32(int dummy): AudioStream(2, inputQueueArray) {} + static void config_spdif3(float fs_Hz); + static audio_block_f32_t *block_left_1st; + static audio_block_f32_t *block_right_1st; + static bool update_responsibility; + static DMAChannel dma; + static void isr(void); +private: + static uint32_t dpll_Gain() __attribute__ ((const)); + static audio_block_f32_t *block_left_2nd; + static audio_block_f32_t *block_right_2nd; + static audio_block_f32_t block_silent; + audio_block_f32_t *inputQueueArray[2]; + static float sample_rate_Hz; +}; + + +#endif From d9efa3143ac925478c00e48262e57b74c1c8ae4b Mon Sep 17 00:00:00 2001 From: joerg Date: Sun, 15 May 2022 19:34:29 +0200 Subject: [PATCH 15/15] full resolution for I2S input, all 32 bits are read and converted to float (instead of only upper 16 bit) --- input_i2s_f32.cpp | 54 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/input_i2s_f32.cpp b/input_i2s_f32.cpp index 365e3a7..e5aa32c 100644 --- a/input_i2s_f32.cpp +++ b/input_i2s_f32.cpp @@ -40,7 +40,7 @@ #include //DMAMEM __attribute__((aligned(32))) -static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; //good for 16-bit audio samples coming in from teh AIC. 32-bit transfers will need this to be bigger. +static uint64_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; // Two 32-bit transfers per sample. audio_block_f32_t * AudioInputI2S_F32::block_left_f32 = NULL; audio_block_f32_t * AudioInputI2S_F32::block_right_f32 = NULL; uint16_t AudioInputI2S_F32::block_offset = 0; @@ -60,7 +60,7 @@ int AudioInputI2S_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES; //#define I2S_BUFFER_TO_USE_BYTES (AudioOutputI2S_F32::audio_block_samples*2*sizeof(i2s_rx_buffer[0])) void AudioInputI2S_F32::begin(void) { - bool transferUsing32bit = false; + bool transferUsing32bit = true; // be official, although this is not cared about begin(transferUsing32bit); } @@ -78,19 +78,19 @@ void AudioInputI2S_F32::begin(bool transferUsing32bit) { #if defined(KINETISK) CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 - dma.TCD->SADDR = (void *)((uint32_t)&I2S0_RDR0 + 2); //From Teensy Audio Library...but why "+ 2" (Chip 2020-10-31) + dma.TCD->SADDR = (void *)((uint32_t)&I2S0_RDR0 + 0); // take the full 32 bit (not just upper half) dma.TCD->SOFF = 0; - dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); - dma.TCD->NBYTES_MLNO = 2; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); + dma.TCD->NBYTES_MLNO = 4; dma.TCD->SLAST = 0; dma.TCD->DADDR = i2s_rx_buffer; - dma.TCD->DOFF = 2; + dma.TCD->DOFF = 4; //dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original from Teensy Audio Library - dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; //dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original from Teensy Audio Library dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES; //dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original from Teensy Audio Library - dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); @@ -101,19 +101,19 @@ void AudioInputI2S_F32::begin(bool transferUsing32bit) { CORE_PIN8_CONFIG = 3; //1:RX_DATA0 IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2; - dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2); + dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 0); dma.TCD->SOFF = 0; - dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); - dma.TCD->NBYTES_MLNO = 2; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); + dma.TCD->NBYTES_MLNO = 4; dma.TCD->SLAST = 0; dma.TCD->DADDR = i2s_rx_buffer; - dma.TCD->DOFF = 2; + dma.TCD->DOFF = 4; //dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original from Teensy Audio Library - dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; //dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original from Teensy Audio Library dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES; //dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original from Teensy Audio Library - dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); @@ -265,7 +265,7 @@ void AudioInputI2S_F32::begin(bool transferUsing32bit) { void AudioInputI2S_F32::isr(void) { uint32_t daddr, offset; - const int16_t *src, *end; + const int32_t *src, *end; //int16_t *dest_left, *dest_right; //audio_block_t *left, *right; float32_t *dest_left_f32, *dest_right_f32; @@ -283,16 +283,16 @@ void AudioInputI2S_F32::isr(void) // need to remove data from the second half //src = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio Library //end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; //original Teensy Audio Library - src = (int16_t *)&i2s_rx_buffer[audio_block_samples/2]; - end = (int16_t *)&i2s_rx_buffer[audio_block_samples]; + src = (int32_t *)&i2s_rx_buffer[audio_block_samples/2]; + end = (int32_t *)&i2s_rx_buffer[audio_block_samples]; update_counter++; //let's increment the counter here to ensure that we get every ISR resulting in audio if (AudioInputI2S_F32::update_responsibility) AudioStream_F32::update_all(); } else { // DMA is receiving to the second half of the buffer // need to remove data from the first half - src = (int16_t *)&i2s_rx_buffer[0]; + src = (int32_t *)&i2s_rx_buffer[0]; //end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio Library - end = (int16_t *)&i2s_rx_buffer[audio_block_samples/2]; + end = (int32_t *)&i2s_rx_buffer[audio_block_samples/2]; } left_f32 = AudioInputI2S_F32::block_left_f32; right_f32 = AudioInputI2S_F32::block_right_f32; @@ -480,8 +480,8 @@ void AudioInputI2S_F32::scale_i32_to_f32( float32_t *p_i32, float32_t *p_f32, in if (!out_f32) return; //scale the float values so that the maximum possible audio values span -1.0 to + 1.0 - //scale_i32_to_f32(out_f32->data, out_f32->data, audio_block_samples); - scale_i16_to_f32(out_f32->data, out_f32->data, audio_block_samples); + scale_i32_to_f32(out_f32->data, out_f32->data, audio_block_samples); + //scale_i16_to_f32(out_f32->data, out_f32->data, audio_block_samples); //prepare to transmit by setting the update_counter (which helps tell if data is skipped or out-of-order) out_f32->id = update_counter; @@ -564,16 +564,16 @@ void AudioInputI2Sslave_F32::begin(void) #if defined(KINETISK) CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 - dma.TCD->SADDR = (void *)((uint32_t)&I2S0_RDR0 + 2); + dma.TCD->SADDR = (void *)((uint32_t)&I2S0_RDR0 + 0); dma.TCD->SOFF = 0; - dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); - dma.TCD->NBYTES_MLNO = 2; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2); + dma.TCD->NBYTES_MLNO = 4; dma.TCD->SLAST = 0; dma.TCD->DADDR = i2s_rx_buffer; - dma.TCD->DOFF = 2; - dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; + dma.TCD->DOFF = 4; + dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 4; dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); - dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; + dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 4; dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);