Brought in Radio Classes and examples

pull/11/head
boblark 4 years ago
parent 0af24d2bef
commit 4fb7a512ab
  1. 20
      AudioEffectDelay_OA_F32.cpp
  2. 16
      AudioEffectDelay_OA_F32.h
  3. 4
      OpenAudio_ArduinoLibrary.h
  4. 72
      examples/TestInputOutput_float/TestInputOutput_float.ino
  5. 11
      examples/TestLoader/TestLoader.ino
  6. 20
      examples/TestLoader2/TestLoader2.ino
  7. 77
      examples/TestOutput_float/TestOutput_float.ino
  8. 46
      memcpy_audio.h
  9. 581
      output_i2s_OA_F32.cpp.xxx
  10. 111
      output_i2s_OA_f32.h.xxx
  11. 1217
      output_i2s_f32.cpp.xxx
  12. 148
      output_i2s_f32.h.xxx
  13. 10
      readme.md
  14. 24
      synth_GaussianWhiteNoise_F32.cpp

@ -59,9 +59,9 @@ void AudioEffectDelay_OA_F32::receiveIncomingData(void) {
int dest_ind = writeposition; //inclusive
if (dest_ind >= (queue[head]->full_length)) {
head++; dest_ind = 0;
if (head >= DELAY_QUEUE_SIZE) head = 0;
if (head >= DELAY_QUEUE_SIZE_OA) head = 0;
if (queue[head] != NULL) {
if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE) tail = 0; }
if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE_OA) tail = 0; }
AudioStream_F32::release(queue[head]);
queue[head]=NULL;
}
@ -94,9 +94,9 @@ void AudioEffectDelay_OA_F32::receiveIncomingData(void) {
//finish writing taking data from the next queue buffer
if (src_count < n_copy) { // there's still more input data to copy...but we need to roll-over to a new input queue
head++; dest_ind = 0;
if (head >= DELAY_QUEUE_SIZE) head = 0;
if (head >= DELAY_QUEUE_SIZE_OA) head = 0;
if (queue[head] != NULL) {
if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE) tail = 0; }
if (head==tail) {tail++; if (tail >= DELAY_QUEUE_SIZE_OA) tail = 0; }
AudioStream_F32::release(queue[head]);
queue[head]=NULL;
}
@ -132,15 +132,15 @@ void AudioEffectDelay_OA_F32::discardUnneededBlocksFromQueue(void) {
if (head >= tail) {
count = head - tail;
} else {
count = DELAY_QUEUE_SIZE + head - tail;
count = DELAY_QUEUE_SIZE_OA + head - tail;
}
/* if (head>0) {
Serial.print("AudioEffectDelay_OA_F32::discardUnneededBlocksFromQueue: head, tail, count, maxblocks, DELAY_QUEUE_SIZE: ");
Serial.print("AudioEffectDelay_OA_F32::discardUnneededBlocksFromQueue: head, tail, count, maxblocks, DELAY_QUEUE_SIZE_OA: ");
Serial.print(head); Serial.print(", ");
Serial.print(tail); Serial.print(", ");
Serial.print(count); Serial.print(", ");
Serial.print(maxblocks); Serial.print(", ");
Serial.print(DELAY_QUEUE_SIZE); Serial.print(", ");
Serial.print(DELAY_QUEUE_SIZE_OA); Serial.print(", ");
Serial.println();
} */
if (count > maxblocks) {
@ -150,7 +150,7 @@ void AudioEffectDelay_OA_F32::discardUnneededBlocksFromQueue(void) {
AudioStream_F32::release(queue[tail]);
queue[tail] = NULL;
}
if (++tail >= DELAY_QUEUE_SIZE) tail = 0;
if (++tail >= DELAY_QUEUE_SIZE_OA) tail = 0;
} while (--count > 0);
}
tailindex = tail;
@ -176,7 +176,7 @@ void AudioEffectDelay_OA_F32::transmitOutgoingData(void) {
//receiveIncomingData method. We'll adjust it next
uint32_t offset_samp = delay_samps[channel]+output->length;
if (ref_samp_long < offset_samp) { //when (ref_samp_long - offset_samp) goes negative, the uint32_t will fail, so we do this logic check
ref_samp_long = ref_samp_long + (((uint32_t)(DELAY_QUEUE_SIZE))*((uint32_t)AUDIO_BLOCK_SIZE_F32));
ref_samp_long = ref_samp_long + (((uint32_t)(DELAY_QUEUE_SIZE_OA))*((uint32_t)AUDIO_BLOCK_SIZE_F32));
}
ref_samp_long = ref_samp_long - offset_samp;
uint16_t source_queue_ind = (uint16_t)(ref_samp_long / ((uint32_t)AUDIO_BLOCK_SIZE_F32));
@ -207,7 +207,7 @@ void AudioEffectDelay_OA_F32::transmitOutgoingData(void) {
//yes, we need to keep filling the output
int Iend = n_output - dest_counter; //how many more data points do we need
source_queue_ind++; source_samp = 0; //which source block will we draw from (and reset the source sample counter)
if (source_queue_ind >= DELAY_QUEUE_SIZE) source_queue_ind = 0; //wrap around on our source black.
if (source_queue_ind >= DELAY_QUEUE_SIZE_OA) source_queue_ind = 0; //wrap around on our source black.
source_block = queue[source_queue_ind]; //get the source block
if (source_block == NULL) { //does it have data?
//no, it doesn't have data. fill destination with zeros

@ -41,19 +41,19 @@
// Are these too big??? I think the same as I16---half as much? <<<<<<<<<<<<<<<<
#if defined(__IMXRT1062__)
// 4.00 second maximum on Teensy 4.0
#define DELAY_QUEUE_SIZE (176512 / AUDIO_BLOCK_SAMPLES)
#define DELAY_QUEUE_SIZE_OA (176512 / AUDIO_BLOCK_SAMPLES)
#elif defined(__MK66FX1M0__)
// 2.41 second maximum on Teensy 3.6
#define DELAY_QUEUE_SIZE (106496 / AUDIO_BLOCK_SIZE_F32)
#define DELAY_QUEUE_SIZE_OA (106496 / AUDIO_BLOCK_SIZE_F32)
#elif defined(__MK64FX512__)
// 1.67 second maximum on Teensy 3.5
#define DELAY_QUEUE_SIZE (73728 / AUDIO_BLOCK_SIZE_F32)
#define DELAY_QUEUE_SIZE_OA (73728 / AUDIO_BLOCK_SIZE_F32)
#elif defined(__MK20DX256__)
// 0.45 second maximum on Teensy 3.1 & 3.2
#define DELAY_QUEUE_SIZE (19826 / AUDIO_BLOCK_SIZE_F32)
#define DELAY_QUEUE_SIZE_OA (19826 / AUDIO_BLOCK_SIZE_F32)
#else
// 0.14 second maximum on Teensy 3.0
#define DELAY_QUEUE_SIZE (6144 / AUDIO_BLOCK_SIZE_F32)
#define DELAY_QUEUE_SIZE_OA (6144 / AUDIO_BLOCK_SIZE_F32)
#endif
class AudioEffectDelay_OA_F32 : public AudioStream_F32
@ -87,7 +87,7 @@ public:
if (channel >= 8) return;
if (milliseconds < 0.0) milliseconds = 0.0;
uint32_t n = (milliseconds*(sampleRate_Hz/1000.0))+0.5;
uint32_t nmax = AUDIO_BLOCK_SIZE_F32 * (DELAY_QUEUE_SIZE-1);
uint32_t nmax = AUDIO_BLOCK_SIZE_F32 * (DELAY_QUEUE_SIZE_OA-1);
if (n > nmax) n = nmax;
uint32_t blks = (n + (AUDIO_BLOCK_SIZE_F32-1)) / AUDIO_BLOCK_SIZE_F32 + 1;
if (!(activemask & (1<<channel))) {
@ -132,14 +132,14 @@ private:
uint16_t headindex; // head index (incoming) data in queue
uint16_t tailindex; // tail index (outgoing) data from queue
uint16_t maxblocks; // number of blocks needed in queue
//#if DELAY_QUEUE_SIZE * AUDIO_BLOCK_SAMPLES < 65535
//#if DELAY_QUEUE_SIZE_OA * AUDIO_BLOCK_SAMPLES < 65535
// uint16_t writeposition;
// uint16_t delay_samps[8]; // # of samples to delay for each channel
//#else
int writeposition; //position within current head buffer in the queue
uint32_t delay_samps[8]; // # of samples to delay for each channel
//#endif
audio_block_f32_t *queue[DELAY_QUEUE_SIZE];
audio_block_f32_t *queue[DELAY_QUEUE_SIZE_OA];
audio_block_f32_t *inputQueueArray[1];
float sampleRate_Hz = AUDIO_SAMPLE_RATE_EXACT; //default. from AudioStream.h??
//int audio_block_len_samples = AUDIO_BLOCK_SAMPLES;

@ -17,8 +17,8 @@
#include "AudioMixer_F32.h"
#include "AudioMultiply_F32.h"
#include "AudioSettings_F32.h"
// #include "input_i2s_f32.h" needs fixing, set to .xxx
// #include "output_i2s_f32.h" needs fixing, set to .xxx
//#include "input_i2s_f32.h"
#include "output_i2s_OA_f32.h"
#include "play_queue_f32.h"
#include "record_queue_f32.h"
#include "synth_pinknoise_f32.h"

@ -0,0 +1,72 @@
#include <Audio.h>
#include <OpenAudio_ArduinoLibrary.h>
#define USE_F32_IO 1
AudioControlSGTL5000 sgtl5000_1;
#if USE_F32_IO
#if USE_CODEC_IN
#else // Use Sine Input
AudioSynthWaveformSine_F32 sine1;
AudioConvert_F32toI16 float2Int1; //, float2Int2;
AudioOutputI2S i2sOut;
//AudioOutputI2S_OA_F32 i2sOut;
/*
//Make all of the audio connections
AudioConnection patchCord1(i2s_in, 0, int2Float1, 0);
AudioConnection patchCord2(i2s_in, 1, int2Float2, 0);
AudioConnection_F32 patchCord10(int2Float1, 0, gain1, 0);
AudioConnection_F32 patchCord11(int2Float2, 0, gain2, 0);
* */
AudioConnection_F32 pc1(sine1, 0, float2Int1, 0);
AudioConnection_F32 patchCord12(float2Int1, 0, i2sOut, 0);
//AudioConnection_F32 patchCord13(gain2, 0, float2Int2, 0);
//AudioConnection_F32 patchCord20(sine1, 0, i2sOut, 0);
//AudioConnection_F32 patchCord21(sine1, 0, i2sOut, 1);
//AudioConnection_F32 patchCord12(gain1, 0, i2sOut, 0);
//AudioConnection_F32 patchCord13(gain2, 0, i2sOut, 1);
/*AudioInputI2S i2s_in;
AudioConvert_I16toF32 int2Float1, int2Float2;
AudioEffectGain_F32 gain1, gain2;
* */
void setup(void) {
Serial.begin(1); delay(1000);
Serial.println("Open Audio Test Input and Output");
AudioMemory(10);
AudioMemory_F32(10);
sgtl5000_1.enable();
sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
sine1.frequency(300.0);
sine1.amplitude(0.01f);
sine1.begin();
/*
//sgtl5000_1.adcHighPassFilterEnable(); //LOW OUTPUT NO WHINEY NOISE, gain has no effect---Direct path??
//reduces noise. https://forum.pjrc.com/threads/27215-24-bit-audio-boards?p=78831&viewfull=1#post78831
sgtl5000_1.adcHighPassFilterDisable(); gain1.setGain_dB(40); // NOISE WITH WHINEY PITCH
gain2.setGain_dB(40);
*/
}
void loop() {
}

@ -0,0 +1,11 @@
/* TestLoader.ino Bob Larkin 3 July 2020
*/
void setup(void) {
Serial.begin(9600);
while(!Serial) ;
Serial.println("Test Loader");
}
void loop() {
}

@ -0,0 +1,20 @@
#include <Audio.h>
AudioSynthWaveformSine sine1;
AudioOutputI2S i2sOut;
AudioConnection patchCord1(sine1, 0, i2sOut, 0);
AudioConnection patchCord2(sine1, 0, i2sOut, 1);
AudioControlSGTL5000 sgtl5000_1;
void setup(void) {
Serial.begin(1); delay(1000);
Serial.println("Teensy Audio, Test Loader");
AudioMemory(10);
sgtl5000_1.enable();
sine1.frequency(300.0);
sine1.amplitude(0.005f);
}
void loop() {
}

@ -0,0 +1,77 @@
/* TestOutput_float.ino Bob Larkin 3 July 2020
*
* Test for #define USE_F32_IO either 0 or 1, all OK on T3.6
*
*/
#include <Audio.h>
#include <OpenAudio_ArduinoLibrary.h>
// NOT WORKING for USE_F32_IO 1 <<<<<<<<<<<<<<<
#define ALL_TEENSY_AUDIO 0
#define USE_F32_IO 1
#if ALL_TEENSY_AUDIO
AudioSynthWaveformSine sine1;
AudioOutputI2S i2sOut;
AudioConnection patchCord1(sine1, 0, i2sOut, 0);
AudioConnection patchCord2(sine1, 0, i2sOut, 1);
AudioControlSGTL5000 sgtl5000_1;
void setup(void) {
Serial.begin(1); delay(1000);
Serial.println("Teensy Audio, No F32");
AudioMemory(10);
sgtl5000_1.enable();
sine1.frequency(300.0);
sine1.amplitude(0.005f);
}
void loop() {
}
// ================================================
#else // OpenAudio F32
#if USE_F32_IO
AudioSynthWaveformSine_F32 sine1;
AudioOutputI2S_OA_F32 i2sOut;
AudioConnection_F32 patchCord1(sine1, 0, i2sOut, 0);
AudioConnection_F32 patchCord2(sine1, 0, i2sOut, 1);
#else // Use F32toI16 convert and I16 out
AudioSynthWaveformSine_F32 sine1;
AudioConvert_F32toI16 float2Int1, float2Int2;
AudioOutputI2S i2sOut;
AudioConnection_F32 patchCord5(sine1, 0, float2Int1, 0);
AudioConnection_F32 patchCord6(sine1, 0, float2Int2, 0);
AudioConnection patchCord7(float2Int1, 0, i2sOut, 0);
AudioConnection patchCord8(float2Int2, 0, i2sOut, 1);
#endif
AudioControlSGTL5000 sgtl5000_1;
void setup(void) {
Serial.begin(1); delay(1000);
#if USE_F32_IO
Serial.println("Open Audio: Test direct F32 Output");
#else
Serial.println("Open Audio: Test Convert to Teensy Audio I16 Output");
#endif
AudioMemory(10);
AudioMemory_F32(10);
//delay(1); Serial.println("Start i2s_f32 out");
//i2sOut.begin();
//delay(1); Serial.println("Start codec");
sgtl5000_1.enable();
sine1.frequency(300.0);
sine1.amplitude(0.005f);
sine1.begin();
}
void loop() {
}
#endif // ALL_TEENSY_AUDIO

@ -0,0 +1,46 @@
/* Teensyduino Audio Memcpy
* Copyright (c) 2016 Frank Bösing
*
* 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:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* 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 memcpy_audio_h_
#define memcpy_audio_h_
#ifdef __cplusplus
extern "C" {
#endif
void memcpy_tointerleaveLR(int16_t *dst, const int16_t *srcL, const int16_t *srcR);
void memcpy_tointerleaveL(int16_t *dst, const int16_t *srcL);
void memcpy_tointerleaveR(int16_t *dst, const int16_t *srcR);
void memcpy_tointerleaveQuad(int16_t *dst, const int16_t *src1, const int16_t *src2,
const int16_t *src3, const int16_t *src4);
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,581 @@
/* output_i2s_OA_f32.cpp
*
* This is Teensy Audio output_i2s.h altered to support OpenAudio float (F32)
* to allow direct output of F32 blocks to the codec. It is the Teensy Audio output
* class with a conversion of float to int16 at the beginning. Bob Larkin
* June 2020
*
* Additions under MIT license, and all the original is:
* Copyright (c) 2014, 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.
*/
#include <Arduino.h>
#include "output_i2s_OA_f32.h"
#include "memcpy_audio.h"
audio_block_t * AudioOutputI2S_OA_F32::block_left_1st = NULL;
audio_block_t * AudioOutputI2S_OA_F32::block_right_1st = NULL;
audio_block_t * AudioOutputI2S_OA_F32::block_left_2nd = NULL;
audio_block_t * AudioOutputI2S_OA_F32::block_right_2nd = NULL;
uint16_t AudioOutputI2S_OA_F32::block_left_offset = 0;
uint16_t AudioOutputI2S_OA_F32::block_right_offset = 0;
bool AudioOutputI2S_OA_F32::update_responsibility = false;
DMAChannel AudioOutputI2S_OA_F32::dma(false);
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
#if defined(__IMXRT1062__)
#include "utility/imxrt_hw.h"
#endif
void AudioOutputI2S_OA_F32::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
block_left_1st = NULL;
block_right_1st = NULL;
config_i2s();
#if defined(KINETISK)
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->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
dma.enable();
I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#elif defined(__IMXRT1062__)
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->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif
update_responsibility = update_setup();
dma.attachInterrupt(isr);
} // end begin()
void AudioOutputI2S_OA_F32::isr(void)
{
#if defined(KINETISK) || defined(__IMXRT1062__)
int16_t *dest;
audio_block_t *blockL, *blockR;
uint32_t saddr, offsetL, offsetR;
saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) {
// 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];
if (AudioOutputI2S_OA_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;
}
blockL = AudioOutputI2S_OA_F32::block_left_1st; // These 2 are I16*
blockR = AudioOutputI2S_OA_F32::block_right_1st;
offsetL = AudioOutputI2S_OA_F32::block_left_offset;
offsetR = AudioOutputI2S_OA_F32::block_right_offset;
if (blockL && blockR) {
memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
offsetL += AUDIO_BLOCK_SAMPLES / 2;
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else if (blockL) {
memcpy_tointerleaveL(dest, blockL->data + offsetL);
offsetL += AUDIO_BLOCK_SAMPLES / 2;
} else if (blockR) {
memcpy_tointerleaveR(dest, blockR->data + offsetR);
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else {
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
}
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 );
if (offsetL < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S_OA_F32::block_left_offset = offsetL;
} else {
AudioOutputI2S_OA_F32::block_left_offset = 0;
AudioStream::release(blockL);
AudioOutputI2S_OA_F32::block_left_1st = AudioOutputI2S_OA_F32::block_left_2nd;
AudioOutputI2S_OA_F32::block_left_2nd = NULL;
}
if (offsetR < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S_OA_F32::block_right_offset = offsetR;
} else {
AudioOutputI2S_OA_F32::block_right_offset = 0;
AudioStream::release(blockR);
AudioOutputI2S_OA_F32::block_right_1st = AudioOutputI2S_OA_F32::block_right_2nd;
AudioOutputI2S_OA_F32::block_right_2nd = NULL;
}
#else
// This is T3.x, x<5. Those would not seem to be candidates for F32 audio processing?
const int16_t *src, *end;
int16_t *dest;
audio_block_t *block;
uint32_t saddr, offset;
saddr = (uint32_t)(dma.CFG->SAR);
dma.clearInterrupt();
if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) {
// 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];
end = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
if (AudioOutputI2S_OA_F32::update_responsibility) AudioStream::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;
end = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
}
block = AudioOutputI2S_OA_F32::block_left_1st;
if (block) {
offset = AudioOutputI2S_OA_F32::block_left_offset;
src = &block->data[offset];
do {
*dest = *src++;
dest += 2;
} while (dest < end);
offset += AUDIO_BLOCK_SAMPLES/2;
if (offset < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S_OA_F32::block_left_offset = offset;
} else {
AudioOutputI2S_OA_F32::block_left_offset = 0;
AudioStream::release(block);
AudioOutputI2S_OA_F32::block_left_1st = AudioOutputI2S_OA_F32::block_left_2nd;
AudioOutputI2S_OA_F32::block_left_2nd = NULL;
}
} else {
do {
*dest = 0;
dest += 2;
} while (dest < end);
}
dest -= AUDIO_BLOCK_SAMPLES - 1;
block = AudioOutputI2S_OA_F32::block_right_1st;
if (block) {
offset = AudioOutputI2S_OA_F32::block_right_offset;
src = &block->data[offset];
do {
*dest = *src++;
dest += 2;
} while (dest < end);
offset += AUDIO_BLOCK_SAMPLES/2;
if (offset < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S_OA_F32::block_right_offset = offset;
} else {
AudioOutputI2S_OA_F32::block_right_offset = 0;
AudioStream::release(block);
AudioOutputI2S_OA_F32::block_right_1st = AudioOutputI2S_OA_F32::block_right_2nd;
AudioOutputI2S_OA_F32::block_right_2nd = NULL;
}
} else {
do {
*dest = 0;
dest += 2;
} while (dest < end);
}
#endif
}
void AudioOutputI2S_OA_F32::update(void)
{
audio_block_f32_t *blockF32;
audio_block_t *blockI16;
blockF32 = AudioStream_F32::receiveReadOnly_f32(0); // input 0 = left
blockI16 = AudioStream::allocate();
if (blockF32) {
// Change F32 to I16
for(int i=0; i<128; i++) {
blockI16->data[i] = (int16_t)(32768.0f*blockF32->data[i]);
}
AudioStream_F32::release(blockF32); // End of F32 activity
// From here down to end of left channel is the same as for I16 Teensy Audio
// Now I16, so sort out the DMA data
__disable_irq();
if (block_left_1st == NULL) {
block_left_1st = blockI16;
block_left_offset = 0;
__enable_irq();
} else if (block_left_2nd == NULL) {
block_left_2nd = blockI16;
__enable_irq();
} else {
audio_block_t *tmp = block_left_1st;
block_left_1st = block_left_2nd;
block_left_2nd = blockI16;
block_left_offset = 0;
__enable_irq();
AudioStream::release(tmp);
}
}
blockF32 = AudioStream_F32::receiveReadOnly_f32(1); // input 1 = right
if (blockF32) {
for(int i=0; i<128; i++) {
blockI16->data[i] = (int16_t)(32768.0f*blockF32->data[i]);
}
AudioStream_F32::release(blockF32); // Second end of F32 activity
__disable_irq();
if (block_right_1st == NULL) {
block_right_1st = blockI16;
block_right_offset = 0;
__enable_irq();
} else if (block_right_2nd == NULL) {
block_right_2nd = blockI16;
__enable_irq();
} else {
audio_block_t *tmp = block_right_1st;
block_right_1st = block_right_2nd;
block_right_2nd = blockI16;
block_right_offset = 0;
__enable_irq();
AudioStream::release(tmp);
}
}
AudioStream::release(blockI16);
} // end update()
#if defined(KINETISK) || defined(KINETISL)
// MCLK needs to be 48e6 / 1088 * 256 = 11.29411765 MHz -> 44.117647 kHz sample rate
//
#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
void AudioOutputI2S_OA_F32::config_i2s(void)
{
#if defined(KINETISK) || defined(KINETISL)
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
// if either transmitter or receiver is enabled, do nothing
if (I2S0_TCSR & I2S_TCSR_TE) return;
if (I2S0_RCSR & I2S_RCSR_RE) return;
// 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));
// configure transmitter
I2S0_TMR = 0;
I2S0_TCR1 = I2S_TCR1_TFW(1); // watermark at half fifo size
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
| I2S_TCR2_BCD | I2S_TCR2_DIV(1);
I2S0_TCR3 = I2S_TCR3_TCE;
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD;
I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
// configure receiver (sync'd to transmitter clocks)
I2S0_RMR = 0;
I2S0_RCR1 = I2S_RCR1_RFW(1);
I2S0_RCR2 = I2S_RCR2_SYNC(1) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
| I2S_RCR2_BCD | I2S_RCR2_DIV(1);
I2S0_RCR3 = I2S_RCR3_RCE;
I2S0_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
// configure pin mux for 3 clock signals
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
#elif defined(__IMXRT1062__)
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT;
// 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);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f
// Select 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));
CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC
int rsync = 0;
int tsync = 1;
I2S1_TMR = 0;
//I2S1_TCSR = (1<<25); //Reset
I2S1_TCR1 = I2S_TCR1_RFW(1);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF
| I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1));
I2S1_RMR = 0;
//I2S1_RCSR = (1<<25); //Reset
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1));
#endif
}
/******************************************************************/
void AudioOutputI2Sslave_OA_F32::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
block_left_1st = NULL;
block_right_1st = NULL;
AudioOutputI2Sslave_OA_F32::config_i2s();
#if defined(KINETISK)
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->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = (void *)((uint32_t)&I2S0_TDR0 + 2);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
dma.enable();
I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#elif defined(__IMXRT1062__)
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->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2);
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif
update_responsibility = update_setup();
dma.attachInterrupt(isr);
}
void AudioOutputI2Sslave_OA_F32::config_i2s(void)
{
#if defined(KINETISK)
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
// if either transmitter or receiver is enabled, do nothing
if (I2S0_TCSR & I2S_TCSR_TE) return;
if (I2S0_RCSR & I2S_RCSR_RE) return;
// Select input clock 0
// Configure to input the bit-clock from pin, bypasses the MCLK divider
I2S0_MCR = I2S_MCR_MICS(0);
I2S0_MDR = 0;
// configure transmitter
I2S0_TMR = 0;
I2S0_TCR1 = I2S_TCR1_TFW(1); // watermark at half fifo size
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP;
I2S0_TCR3 = I2S_TCR3_TCE;
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S0_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
// configure receiver (sync'd to transmitter clocks)
I2S0_RMR = 0;
I2S0_RCR1 = I2S_RCR1_RFW(1);
I2S0_RCR2 = I2S_RCR2_SYNC(1) | I2S_TCR2_BCP;
I2S0_RCR3 = I2S_RCR3_RCE;
I2S0_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
// configure pin mux for 3 clock signals
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
#elif defined(__IMXRT1062__)
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;
// not using MCLK in slave mode - hope that's ok?
//CORE_PIN23_CONFIG = 3; // AD_B1_09 ALT3=SAI1_MCLK
CORE_PIN21_CONFIG = 3; // AD_B1_11 ALT3=SAI1_RX_BCLK
CORE_PIN20_CONFIG = 3; // AD_B1_10 ALT3=SAI1_RX_SYNC
IOMUXC_SAI1_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868
IOMUXC_SAI1_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872
// configure transmitter
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(1); // watermark at half fifo size
I2S1_TCR2 = I2S_TCR2_SYNC(1) | I2S_TCR2_BCP;
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_RCR4_FSD;
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
// configure receiver
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP;
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP;
I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
#endif
}

@ -0,0 +1,111 @@
/* output_i2s_OA_f32.h
*
* This is Teensy Audio output_i2s.h altered to support OpenAudio float (F32)
* to allow direct output of F32 blocks to the codec. It is the Teensy Audio output
* class with a conversion of float to int16 at the beginning. Bob Larkin
* June 2020
*
* This is basic: 128 word blocks, 16-bit integer word to the codec. It
* needs to be revisited for variable word block.
*
* Tested: Using TestOutput_float.ino, Runs T3.6 and T4.0 w/o error.
*
* Additions under MIT license, and all the original is:
* Copyright (c) 2014, 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.
*/
#ifndef output_i2s_OA_F32_h_
#define output_i2s_OA_F32_h_
#include "Arduino.h"
#include "AudioStream.h"
#include "AudioStream_F32.h"
#include "DMAChannel.h"
class AudioOutputI2S_OA_F32 : public AudioStream_F32
{
//GUI: inputs:2, outputs:0 //this line used for automatic generation of GUI node
public:
AudioOutputI2S_OA_F32(void) : AudioStream_F32(2, inputQueueArray) {
// Add 2 delays as experimental fix for possible Teensy Loader issue.
uint32_t ii;
uint32_t xx = 0;
for (ii=0; ii<1000; ii++) xx += ii;
begin();
for (ii=0; ii<1000; ii++) xx += ii;
}
/* This long form needs to be added <<<<<<<<<<<<<<<<
AudioOutputI2S_OA_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray)
{
sample_rate_Hz = settings.sample_rate_Hz;
audio_block_samples = settings.audio_block_samples;
begin();
}
*/
virtual void update(void);
void begin(void);
friend class AudioInputI2S_OA_F32;
/* These are 4, 6, and 8 channel options in I16 T4, not yet supported in F32
#if defined(__IMXRT1062__)
friend class AudioOutputI2SQuad;
friend class AudioInputI2SQuad;
friend class AudioOutputI2SHex;
friend class AudioInputI2SHex;
friend class AudioOutputI2SOct;
friend class AudioInputI2SOct;
#endif
*/
protected:
// Next fcn to be used only inside AudioOutputI2S_OA_F32slave !!
AudioOutputI2S_OA_F32(int dummy): AudioStream_F32(2, inputQueueArray) {}
static void config_i2s(void);
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_left_2nd;
static audio_block_t *block_right_2nd;
static uint16_t block_left_offset;
static uint16_t block_right_offset;
audio_block_f32_t *inputQueueArray[2];
};
class AudioOutputI2Sslave_OA_F32 : public AudioOutputI2S_OA_F32
{
public:
AudioOutputI2Sslave_OA_F32(void) : AudioOutputI2S_OA_F32(0) { begin(); } ;
void begin(void);
friend class AudioInputI2Sslave_OA_F32;
friend void dma_ch0_isr(void);
protected:
static void config_i2s(void);
};
#endif

File diff suppressed because it is too large Load Diff

@ -1,4 +1,9 @@
/* Audio Library for Teensy 3.X
/* output_i2s_f32.h - Input block of float samples from I2S
*
* Adapted to F32 output and Open Audio AudioSettings_F32 by Chip Audette
* Modified for Teensy 4.x Bob Larkin June 2020
*
* Direct from: Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
@ -23,13 +28,17 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/* Extended by Chip Audette, OpenAudio, May 2019
* Converted to F32 and to variable audio block length
* The F32 conversion is under the MIT License. Use at your own risk.
*/
#ifndef output_i2s_f32_h_
#define output_i2s_f32_h_
#include "Arduino.h"
#include "AudioStream_F32.h"
#include "AudioStream.h"
//include "AudioStream.h"
#include "DMAChannel.h"
@ -46,20 +55,39 @@ public:
}
virtual void update(void);
void begin(void);
void begin(bool);
void sub_begin_i32(void);
void sub_begin_i16(void);
friend class AudioInputI2S_F32;
static void convert_f32_to_i16( float32_t *p_f32, int16_t *p_i16, int len) ;
static void scale_f32_to_i16( float32_t *p_f32, float32_t *p_i16, int len) ;
static void scale_f32_to_i24( float32_t *p_f32, float32_t *p_i16, int len) ;
static void scale_f32_to_i32( float32_t *p_f32, float32_t *p_i32, int len) ;
static float setI2SFreq(const float);
/* I16 version supported these for T4:
#if defined(__IMXRT1062__)
friend class AudioOutputI2SQuad;
friend class AudioInputI2SQuad;
friend class AudioOutputI2SHex;
friend class AudioInputI2SHex;
friend class AudioOutputI2SOct;
friend class AudioInputI2SOct;
#endif
*/
protected:
//AudioOutputI2S_F32(const AudioSettings &settings): AudioStream_F32(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
static void config_i2s(void);
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;
static void config_i2s(bool);
static void config_i2s_i16(void);
static void config_i2s_i32(void);
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);
static void isr_16(void);
static void isr_32(void);
private:
static audio_block_t *block_left_2nd;
static audio_block_t *block_right_2nd;
static audio_block_f32_t *block_left_2nd;
static audio_block_f32_t *block_right_2nd;
static uint16_t block_left_offset;
static uint16_t block_right_offset;
audio_block_f32_t *inputQueueArray[2];
@ -67,8 +95,110 @@ private:
static int audio_block_samples;
volatile uint8_t enabled = 1;
};
#endif
#if 0
/////////////////////////SAVE //////////////////////
/* output_i2s_f32.h - Input block of float samples from I2S
*
* Adapted to F32 output and Open Audio AudioSettings_F32 by Chip Audette
* Modified for Teensy 4.x Bob Larkin June 2020
*
* Direct from:
* Audio Library for Teensy 3.X
* Copyright (c) 2014, 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.
*/
#ifndef output_i2s_f32_h_
#define output_i2s_f32_h_
#include "Arduino.h"
#include "AudioStream_F32.h"
#include "AudioStream.h"
#include "DMAChannel.h"
class AudioOutputI2S_F32 : public AudioStream_F32
{
//GUI: inputs:2, outputs:0 //this line used for automatic generation of GUI node
public:
// To use default AUDIO_SAMPLE_RATE and BLOCK_SIZE_SAMPLES from AudioStream.h:
AudioOutputI2S_F32(void) : AudioStream_F32(2, inputQueueArray) { begin();}
// Or, to allow for change in either:
AudioOutputI2S_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray) {
sample_rate_Hz = settings.sample_rate_Hz;
audio_block_samples = settings.audio_block_samples;
begin();
}
virtual void update(void);
void begin(void);
friend class AudioInputI2S_F32;
static void convert_f32_to_i16( float32_t *p_f32, int16_t *p_i16, int len) ; // NOT IN TYMPAN
/* I16 version supported these for T4:
#if defined(__IMXRT1062__)
friend class AudioOutputI2SQuad;
friend class AudioInputI2SQuad;
friend class AudioOutputI2SHex;
friend class AudioInputI2SHex;
friend class AudioOutputI2SOct;
friend class AudioInputI2SOct;
#endif
*/
///////////////////TYMPAN public: /////////////////
//void begin(void);
void begin(bool);
void sub_begin_i32(void);
void sub_begin_i16(void);
friend class AudioInputI2S_F32;
static void scale_f32_to_i16( float32_t *p_f32, float32_t *p_i16, int len) ;
static void scale_f32_to_i24( float32_t *p_f32, float32_t *p_i16, int len) ;
static void scale_f32_to_i32( float32_t *p_f32, float32_t *p_i32, int len) ;
static float setI2SFreq(const float);
////////////////////////////////////////////////
protected:
//AudioOutputI2S_F32(const AudioSettings &settings): AudioStream_F32(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
static void config_i2s(void);
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_f32(void);
private:
static audio_block_f32_t *block_left_2nd;
static audio_block_f32_t *block_right_2nd;
static uint16_t block_left_offset;
static uint16_t block_right_offset;
audio_block_f32_t *inputQueueArray[2];
static float sample_rate_Hz;
static int audio_block_samples;
volatile uint8_t enabled = 1;
};
#endif
#endif
// #if 0

@ -31,7 +31,15 @@ OpenAudio Library for Teensy
26-Brought in RadioFMDetector_F32 and Example
27-Brought in synth_sin_cos_F32 and test example
28-Brought in RadioNoiseBlanker_F32 and Example
29-Created output_i2s_OA_F32.h and .cpp to have F32 input. Work in Progress, DO NOT USE. Set to .xxx
**Special Note 2*** 2 July 2020 - Recently, the input and output I2S for F32 have been disabled as they had major hardware problems with
Teensy 4.x. The previous output_i2s_f32 files supported variable sample rate and variable block size, but only
for T3.x. To get this going again, the old classes will be re-enabled for T3.x only . And, new classes have been
created that compile and run under T4.x (and T3.5, T3.6) but, for now, have no provision for changing block size, sample rate or ADC
output word (always 16-bit). The new files are output_i2s_OA_F32.h and output_i2s_OA_F32.cpp with the corresponding inputs to come soon. The classes are AudioInputI2S_OA_F32 and AudioOutputI2S_OA_F32 with the same functionality as their parallel 16-bit
integer classes under Teensy Audio. If you need variable block size and/or variable data rate (T3.x only) use the Convert_I16toF32 and
Convert_F32toU16 classes.
**Purpose**: The purpose of this library is to build upon the [Teensy Audio Library](http://www.pjrc.com/teensy/td_libs_Audio.html) to enable new functionality for real-time audio processing.

@ -58,23 +58,29 @@ void AudioSynthGaussian_F32::update(void)
* 2nd ed, with the comment "this is about as good as any 32-bit linear
* congruential generator, entirely adequate for many uses."
*/
// Try:
union {
uint32_t i32;
float32_t f32;
} uinf;
for(int i=0; i<blockSize; i++) {
rdev = 0.0f;
for (int j=0; j<12; j++){ // Add 12, using Central Limit to get Gaussian
idum = (uint32_t)1664525 * idum + (uint32_t)1013904223;
it = FL_ONE | (FL_MASK & idum); // Generate random number
rdev += (*(float *)&it) - 1.0f; // Cute convert to float
/* ====================================
union (
uint32_t i32;
float32_t f32;
) uinf;
// rdev += (*(float *)&it) - 1.0f; // Cute convert to float - Gets warning
// Try
uinf.i32 = it;
rdev += uinf.f32 - 1.0f;
===================================
*/
}
// Next, to get general form
// return mu + sd * 3.4641016f * (rdev - 0.5*(float)M) / sqrtf((float32_t)M);

Loading…
Cancel
Save