From 89afca0c8f97c52e0398c958faa2a6f46b638843 Mon Sep 17 00:00:00 2001 From: boblark Date: Wed, 6 Jan 2021 15:05:43 -0800 Subject: [PATCH] Add i/o F32 from Chip Audette's Tympan library. --- AudioEffectCompWDRC_F32.h | 65 +- AudioMultiply_F32.h | 14 +- AudioSettings_F32.cpp | 36 +- AudioStream_F32.cpp | 64 +- OpenAudio_ArduinoLibrary.h | 4 +- examples/PassthroughF32/PassthroughF32.ino | 50 + examples/SineCosOut/SineCosOut.ino | 44 + input_i2s_f32.cpp | 1316 +++++++++++++++ input_i2s_f32.h | 167 ++ output_i2s_f32.cpp | 1681 ++++++++++++++++++++ output_i2s_f32.h | 234 +++ play_queue_f32.cpp | 96 +- play_queue_f32.h | 61 +- synth_GaussianWhiteNoise_F32.cpp | 76 +- synth_sine_f32.cpp | 87 +- 15 files changed, 3756 insertions(+), 239 deletions(-) create mode 100644 examples/PassthroughF32/PassthroughF32.ino create mode 100644 examples/SineCosOut/SineCosOut.ino create mode 100644 input_i2s_f32.cpp create mode 100644 input_i2s_f32.h create mode 100644 output_i2s_f32.cpp create mode 100644 output_i2s_f32.h diff --git a/AudioEffectCompWDRC_F32.h b/AudioEffectCompWDRC_F32.h index 0ee2a70..76404b3 100644 --- a/AudioEffectCompWDRC_F32.h +++ b/AudioEffectCompWDRC_F32.h @@ -1,12 +1,12 @@ /* * AudioEffectCompWDR_F32: Wide Dynamic Rnage Compressor - * + * * Created: Chip Audette (OpenAudio) Feb 2017 * Derived From: WDRC_circuit from CHAPRO from BTNRC: https://github.com/BTNRH/chapro * As of Feb 2017, CHAPRO license is listed as "Creative Commons?" - * + * * MIT License. Use at your own risk. - * + * */ #ifndef _AudioEffectCompWDRC_F32 @@ -25,8 +25,8 @@ class AudioCalcGainWDRC_F32; //forward declared. Actually defined in later hea class AudioEffectCompWDRC_F32 : public AudioStream_F32 { - //GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node - //GUI: shortName: CompressWDRC +//GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node +//GUI: shortName: CompressWDRC public: AudioEffectCompWDRC_F32(void): AudioStream_F32(1,inputQueueArray) { //need to modify this for user to set sample rate setSampleRate_Hz(AUDIO_SAMPLE_RATE); @@ -43,14 +43,14 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 //receive the input audio data audio_block_f32_t *block = AudioStream_F32::receiveReadOnly_f32(); if (!block) return; - + //allocate memory for the output of our algorithm audio_block_f32_t *out_block = AudioStream_F32::allocate_f32(); if (!out_block) return; - + Serial.print("data37= "); Serial.print(block->data[37], 6); //do the algorithm compress(block->data, out_block->data, block->length); - + // transmit the block and release memory AudioStream_F32::transmit(out_block); // send the FIR output AudioStream_F32::release(out_block); @@ -59,7 +59,7 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 //here is the function that does all the work - //void cha_agc_channel(float *input, float *output, int cs) { + //void cha_agc_channel(float *input, float *output, int cs) { // //compress(input, output, cs, &prev_env, // // CHA_DVAR.alfa, CHA_DVAR.beta, CHA_DVAR.tkgain, CHA_DVAR.tk, CHA_DVAR.cr, CHA_DVAR.bolt, CHA_DVAR.maxdB); // compress(input, output, cs); @@ -67,22 +67,23 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 //void compress(float *x, float *y, int n, float *prev_env, // float &alfa, float &beta, float &tkgn, float &tk, float &cr, float &bolt, float &mxdB) - void compress(float *x, float *y, int n) + void compress(float *x, float *y, int n) //x, input, audio waveform data //y, output, audio waveform data after compression //n, input, number of samples in this audio block - { + { // find smoothed envelope audio_block_f32_t *envelope_block = AudioStream_F32::allocate_f32(); if (!envelope_block) return; calcEnvelope.smooth_env(x, envelope_block->data, n); + Serial.print(" Envelope37= "); Serial.print(envelope_block->data[37], 6); //float *xpk = envelope_block->data; //get pointer to the array of (empty) data values //calculate gain audio_block_f32_t *gain_block = AudioStream_F32::allocate_f32(); if (!gain_block) return; calcGain.calcGainFromEnvelope(envelope_block->data, gain_block->data, n); - + Serial.print(" Gain37= "); Serial.println(envelope_block->data[37], 6); //apply gain arm_mult_f32(x, gain_block->data, y, n); @@ -95,7 +96,7 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 void setDefaultValues(void) { //set default values...configure as limitter BTNRH_WDRC::CHA_WDRC gha = { - 5.0f, // attack time (ms) + 5.0f, // attack time (ms) 50.0f, // release time (ms) 24000.0f, // fs, sampling rate (Hz), THIS IS IGNORED! 115.0f, // maxdB, maximum signal (dB SPL)...assumed SPL for full-scale input signal @@ -120,7 +121,7 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 //set all of the user parameters for the compressor //assumes that the sample rate has already been set!!! void setParams(float attack_ms, float release_ms, float maxdB, float tkgain, float comp_ratio, float tk, float bolt) { - + //configure the envelope calculator...assumes that the sample rate has already been set! calcEnvelope.setAttackRelease_msec(attack_ms,release_ms); @@ -133,28 +134,28 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 given_sample_rate_Hz = _fs_Hz; calcEnvelope.setSampleRate_Hz(_fs_Hz); } - - void setAttackRelease_msec(const float atk_msec, const float rel_msec) { - calcEnvelope.setAttackRelease_msec(atk_msec, rel_msec); - } - - void setKneeLimiter_dBSPL(float _bolt) { calcGain.setKneeLimiter_dBSPL(_bolt); } - void setKneeLimiter_dBFS(float _bolt_dBFS) { calcGain.setKneeLimiter_dBFS(_bolt_dBFS); } - void setGain_dB(float _gain_dB) { calcGain.setGain_dB(_gain_dB); } //gain at start of compression - void setKneeCompressor_dBSPL(float _tk) { calcGain.setKneeCompressor_dBSPL(_tk); } - void setKneeCompressor_dBFS(float _tk_dBFS) { calcGain.setKneeCompressor_dBFS(_tk_dBFS); } - void setCompRatio(float _cr) { calcGain.setCompRatio(_cr); } - void setMaxdB(float _maxdB) { calcGain.setMaxdB(_maxdB); }; + + void setAttackRelease_msec(const float atk_msec, const float rel_msec) { + calcEnvelope.setAttackRelease_msec(atk_msec, rel_msec); + } + + void setKneeLimiter_dBSPL(float _bolt) { calcGain.setKneeLimiter_dBSPL(_bolt); } + void setKneeLimiter_dBFS(float _bolt_dBFS) { calcGain.setKneeLimiter_dBFS(_bolt_dBFS); } + void setGain_dB(float _gain_dB) { calcGain.setGain_dB(_gain_dB); } //gain at start of compression + void setKneeCompressor_dBSPL(float _tk) { calcGain.setKneeCompressor_dBSPL(_tk); } + void setKneeCompressor_dBFS(float _tk_dBFS) { calcGain.setKneeCompressor_dBFS(_tk_dBFS); } + void setCompRatio(float _cr) { calcGain.setCompRatio(_cr); } + void setMaxdB(float _maxdB) { calcGain.setMaxdB(_maxdB); }; float getCurrentLevel_dB(void) { return AudioCalcGainWDRC_F32::db2(calcEnvelope.getCurrentLevel()); } //this is 20*log10(abs(signal)) after the envelope smoothing - float getGain_dB(void) { return calcGain.getGain_dB(); } //returns the linear gain of the system - float getCurrentGain(void) { return calcGain.getCurrentGain(); } - float getCurrentGain_dB(void) { return calcGain.getCurrentGain_dB(); } - + float getGain_dB(void) { return calcGain.getGain_dB(); } //returns the linear gain of the system + float getCurrentGain(void) { return calcGain.getCurrentGain(); } + float getCurrentGain_dB(void) { return calcGain.getCurrentGain_dB(); } + AudioCalcEnvelope_F32 calcEnvelope; AudioCalcGainWDRC_F32 calcGain; - + private: audio_block_f32_t *inputQueueArray[1]; float given_sample_rate_Hz; @@ -162,4 +163,4 @@ class AudioEffectCompWDRC_F32 : public AudioStream_F32 #endif - + diff --git a/AudioMultiply_F32.h b/AudioMultiply_F32.h index 3895673..ebdf82b 100644 --- a/AudioMultiply_F32.h +++ b/AudioMultiply_F32.h @@ -1,12 +1,12 @@ /* * AudioMultiply - * + * * Created: Patrick Radius, December 2016 * Purpose: Multiply two channels of audio data. Can be used for example as 'vca' or amplitude modulation. * Assumes floating-point data. - * - * This processes a single stream fo audio data (ie, it is mono) - * + * + * This processes a single stream fo audio data (ie, it is mono) + * * MIT License. use at your own risk. */ #ifndef AUDIOMULTIPLYF32_H @@ -20,10 +20,10 @@ class AudioMultiply_F32 : public AudioStream_F32 //GUI: inputs:2, outputs:1 //this line used for automatic generation of GUI node public: AudioMultiply_F32(void) : AudioStream_F32(2, inputQueueArray_f32) {}; - AudioMultiply_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) {}; - + AudioMultiply_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32) {}; + void update(void); - + private: audio_block_f32_t *inputQueueArray_f32[2]; }; diff --git a/AudioSettings_F32.cpp b/AudioSettings_F32.cpp index ee1b9f4..195cb69 100644 --- a/AudioSettings_F32.cpp +++ b/AudioSettings_F32.cpp @@ -4,26 +4,26 @@ #include "AudioStream_F32.h" float AudioSettings_F32::cpu_load_percent(const int n) { - //n is the number of cycles - #define CYCLE_COUNTER_APPROX_PERCENT(n) (((n) + (F_CPU / 32 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100)) / (F_CPU / 16 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100)) - float foo1 = ((float)(F_CPU / 32))/sample_rate_Hz; - foo1 *= ((float)audio_block_samples); - foo1 /= 100.f; - foo1 += (float)n; - float foo2 = (float)(F_CPU / 16)/sample_rate_Hz; - foo2 *= ((float)audio_block_samples); - foo2 /= 100.f; - return foo1 / foo2; + //n is the number of cycles + // #define CYCLE_COUNTER_APPROX_PERCENT(n) (((n) + (F_CPU / 32 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100)) / (F_CPU / 16 / AUDIO_SAMPLE_RATE * AUDIO_BLOCK_SAMPLES / 100)) removed per @jcj83429 21 Dec 20 defined double + float foo1 = ((float)(F_CPU / 32))/sample_rate_Hz; + foo1 *= ((float)audio_block_samples); + foo1 /= 100.f; + foo1 += (float)n; + float foo2 = (float)(F_CPU / 16)/sample_rate_Hz; + foo2 *= ((float)audio_block_samples); + foo2 /= 100.f; + return foo1 / foo2; - //return (((n) + (F_CPU / 32 / sample_rate_Hz * audio_block_samples / 100)) / (F_CPU / 16 / sample_rate_Hz * audio_block_samples / 100)); + //return (((n) + (F_CPU / 32 / sample_rate_Hz * audio_block_samples / 100)) / (F_CPU / 16 / sample_rate_Hz * audio_block_samples / 100)); } -float AudioSettings_F32::processorUsage(void) { - return cpu_load_percent(AudioStream::cpu_cycles_total); +float AudioSettings_F32::processorUsage(void) { + return cpu_load_percent(AudioStream::cpu_cycles_total); }; -float AudioSettings_F32::processorUsageMax(void) { - return cpu_load_percent(AudioStream::cpu_cycles_total_max); +float AudioSettings_F32::processorUsageMax(void) { + return cpu_load_percent(AudioStream::cpu_cycles_total_max); +} +void AudioSettings_F32::processorUsageMaxReset(void) { + AudioStream::cpu_cycles_total_max = AudioStream::cpu_cycles_total; } -void AudioSettings_F32::processorUsageMaxReset(void) { - AudioStream::cpu_cycles_total_max = AudioStream::cpu_cycles_total; -} \ No newline at end of file diff --git a/AudioStream_F32.cpp b/AudioStream_F32.cpp index fdf8737..7aaf96d 100644 --- a/AudioStream_F32.cpp +++ b/AudioStream_F32.cpp @@ -8,21 +8,21 @@ uint8_t AudioStream_F32::f32_memory_used = 0; uint8_t AudioStream_F32::f32_memory_used_max = 0; audio_block_f32_t* allocate_f32_memory(const int num) { - static bool firstTime=true; - static audio_block_f32_t *data_f32; - if (firstTime == true) { - firstTime = false; - data_f32 = new audio_block_f32_t[num]; - } - return data_f32; + static bool firstTime=true; + static audio_block_f32_t *data_f32; + if (firstTime == true) { + firstTime = false; + data_f32 = new audio_block_f32_t[num]; + } + return data_f32; } void AudioMemory_F32(const int num) { - audio_block_f32_t *data_f32 = allocate_f32_memory(num); - if (data_f32 != NULL) AudioStream_F32::initialize_f32_memory(data_f32, num); + audio_block_f32_t *data_f32 = allocate_f32_memory(num); + if (data_f32 != NULL) AudioStream_F32::initialize_f32_memory(data_f32, num); } void AudioMemory_F32(const int num, const AudioSettings_F32 &settings) { - audio_block_f32_t *data_f32 = allocate_f32_memory(num); - if (data_f32 != NULL) AudioStream_F32::initialize_f32_memory(data_f32, num, settings); + audio_block_f32_t *data_f32 = allocate_f32_memory(num); + if (data_f32 != NULL) AudioStream_F32::initialize_f32_memory(data_f32, num, settings); } // Set up the pool of audio data blocks @@ -31,8 +31,8 @@ void AudioStream_F32::initialize_f32_memory(audio_block_f32_t *data, unsigned in { unsigned int i; - //Serial.println("AudioStream_F32 initialize_memory"); - //delay(10); + // Serial.println("AudioStream_F32 initialize_memory"); + // delay(10); if (num > 192) num = 192; __disable_irq(); f32_memory_pool = data; @@ -42,6 +42,7 @@ void AudioStream_F32::initialize_f32_memory(audio_block_f32_t *data, unsigned in for (i=0; i < num; i++) { f32_memory_pool_available_mask[i >> 5] |= (1 << (i & 0x1F)); } + for (i=0; i < num; i++) { data[i].memory_pool_index = i; } @@ -52,8 +53,8 @@ void AudioStream_F32::initialize_f32_memory(audio_block_f32_t *data, unsigned in { initialize_f32_memory(data,num); for (unsigned int i=0; i < num; i++) { - data[i].fs_Hz = settings.sample_rate_Hz; - data[i].length = settings.audio_block_samples; + data[i].fs_Hz = settings.sample_rate_Hz; + data[i].length = settings.audio_block_samples; } } // end initialize_memory @@ -66,7 +67,19 @@ audio_block_f32_t * AudioStream_F32::allocate_f32(void) audio_block_f32_t *block; uint8_t used; + // f32_memory_pool_available_mask is array of six 32-bit uints with a 1 in position of + // each available block (max 192). 0 if unavailable (busy) or not allocated at all. p = f32_memory_pool_available_mask; + + +/* + if(millis() > 1200) { + Serial.print("AudioStream_F32 "); + Serial.println((uint32_t)*p, BIN); // Just first of 6 + } + */ + + __disable_irq(); do { avail = *p; if (avail) break; @@ -76,7 +89,7 @@ audio_block_f32_t * AudioStream_F32::allocate_f32(void) p++; avail = *p; if (avail) break; p++; avail = *p; if (avail) break; __enable_irq(); - //Serial.println("alloc_f32:null"); + // Serial.println("alloc_f32:null"); return NULL; } while (0); n = __builtin_clz(avail); @@ -88,8 +101,7 @@ audio_block_f32_t * AudioStream_F32::allocate_f32(void) block = f32_memory_pool + ((index << 5) + (31 - n)); block->ref_count = 1; if (used > f32_memory_used_max) f32_memory_used_max = used; - //Serial.print("alloc_f32:"); - //Serial.println((uint32_t)block, HEX); + // Serial.print("alloc_f32:"); Serial.println((uint32_t)block, HEX); return block; } @@ -106,8 +118,7 @@ void AudioStream_F32::release(audio_block_f32_t *block) if (block->ref_count > 1) { block->ref_count--; } else { - //Serial.print("release_f32:"); - //Serial.println((uint32_t)block, HEX); +//Serial.print("release_f32:"); Serial.println((uint32_t)block, HEX); f32_memory_pool_available_mask[index] |= mask; f32_memory_used--; } @@ -125,17 +136,17 @@ void AudioStream_F32::transmit(audio_block_f32_t *block, unsigned char index) { //Serial.print("AudioStream_F32: transmit(). start...index = ");Serial.println(index); for (AudioConnection_F32 *c = destination_list_f32; c != NULL; c = c->next_dest) { - //Serial.print(" : loop1, c->src_index = ");Serial.println(c->src_index); + //Serial.print(" : loop1, c->src_index = ");Serial.println(c->src_index); if (c->src_index == index) { - //Serial.println(" : if1"); + //Serial.println(" : if1"); if (c->dst.inputQueue_f32[c->dest_index] == NULL) { - //Serial.println(" : if2"); + //Serial.println(" : if2"); c->dst.inputQueue_f32[c->dest_index] = block; block->ref_count++; //Serial.print(" : block->ref_count = "); Serial.println(block->ref_count); } } - } + } //Serial.println("AudioStream_F32: transmit(). finished."); } @@ -144,14 +155,13 @@ void AudioStream_F32::transmit(audio_block_f32_t *block, unsigned char index) audio_block_f32_t * AudioStream_F32::receiveReadOnly_f32(unsigned int index) { audio_block_f32_t *in; - +//Serial.print(index); Serial.print(" "); Serial.println(num_inputs_f32); if (index >= num_inputs_f32) return NULL; in = inputQueue_f32[index]; inputQueue_f32[index] = NULL; return in; } - // Receive block from an input. The block will not // be shared, so its contents may be changed. audio_block_f32_t * AudioStream_F32::receiveWritable_f32(unsigned int index) @@ -172,7 +182,7 @@ audio_block_f32_t * AudioStream_F32::receiveWritable_f32(unsigned int index) void AudioConnection_F32::connect(void) { AudioConnection_F32 *p; - + if (dest_index > dst.num_inputs_f32) return; __disable_irq(); p = src.destination_list_f32; diff --git a/OpenAudio_ArduinoLibrary.h b/OpenAudio_ArduinoLibrary.h index d4638ed..5f679b8 100644 --- a/OpenAudio_ArduinoLibrary.h +++ b/OpenAudio_ArduinoLibrary.h @@ -17,8 +17,8 @@ #include "AudioMixer_F32.h" #include "AudioMultiply_F32.h" #include "AudioSettings_F32.h" -//#include "input_i2s_f32.h" -#include "output_i2s_OA_f32.h" +#include "input_i2s_OA_f32.h" +#include "output_i2s_OA_F32.h" #include "play_queue_f32.h" #include "record_queue_f32.h" #include "synth_pinknoise_f32.h" diff --git a/examples/PassthroughF32/PassthroughF32.ino b/examples/PassthroughF32/PassthroughF32.ino new file mode 100644 index 0000000..af12f2e --- /dev/null +++ b/examples/PassthroughF32/PassthroughF32.ino @@ -0,0 +1,50 @@ +/* + PassthroughF32.ino - Stereo passthrough. + Bob Larkin 22 Dec 2020 + + Basic test of I2S input and output. + Tested T3.6 and 5 Jan 2021 + Public Domain + */ +#include "Arduino.h" +#include "Audio.h" // Teensy I16 Audio Library +#include "OpenAudio_ArduinoLibrary.h" // F32 library +#include "AudioStream_F32.h" +/* +#include "Audio.h" // Teensy I16 Audio Library +#include "OpenAudio_ArduinoLibrary.h" // F32 library +#include "AudioStream_F32.h" */ + +// AudioInputI2S_OA_F32 i2sIn; +AudioInputI2S_F32 i2sIn; +AudioAnalyzePeak_F32 peakL; +AudioAnalyzePeak_F32 peakR; +// AudioOutputI2S_OA_F32 i2sOut; +AudioOutputI2S_F32 i2sOut; + +AudioConnection_F32 patchCord0(i2sIn, 0, peakL, 0); +AudioConnection_F32 patchCord1(i2sIn, 1, peakR, 0); +AudioConnection_F32 patchCord2(i2sIn, 0, i2sOut, 0); +AudioConnection_F32 patchCord3(i2sIn, 1, i2sOut, 1); +AudioControlSGTL5000 codec1; + +void setup() { + Serial.begin(9600); // (anything) + delay(1000); + Serial.println("OpenAudio_ArduinoLibrary - Passthrough Stereo"); + + // Internally, I16 blocks are used Bob Fix sometime + // AudioMemory(4); // Needed or I/O + AudioMemory_F32(20); + codec1.enable(); // MUST be before inputSelect() + codec1.inputSelect(AUDIO_INPUT_LINEIN); +} + +void loop() { + Serial.print("Max float memory = "); + Serial.println(AudioStream_F32::f32_memory_used_max); + if(peakL.available()) Serial.print(peakL.read(), 6); + Serial.print(" <-L R-> "); + if(peakR.available()) Serial.println(peakR.read(), 6); + delay(1000); +} diff --git a/examples/SineCosOut/SineCosOut.ino b/examples/SineCosOut/SineCosOut.ino new file mode 100644 index 0000000..2eab88b --- /dev/null +++ b/examples/SineCosOut/SineCosOut.ino @@ -0,0 +1,44 @@ +/* + SineCosOut.ino - Sine left, Cos right + Bob Larkin 28 Dec 2020 + + Basic test of I2S input and output. + Tested on T3.6 and T4.0 + Public Domain + */ +#include "Audio.h" // Teensy I16 Audio Library +#include "OpenAudio_ArduinoLibrary.h" // F32 library +#include "AudioStream_F32.h" + +AudioSynthSineCosine_F32 sineCos; +AudioAnalyzePeak_F32 peakL; +AudioAnalyzePeak_F32 peakR; +//AudioOutputI2S_OA_F32 i2sOut; +AudioOutputI2S_F32 i2sOut; +AudioConnection_F32 patchCord0(sineCos, 0, peakL, 0); +AudioConnection_F32 patchCord1(sineCos, 1, peakR, 0); +AudioConnection_F32 patchCord2(sineCos, 0, i2sOut, 0); // Sine +AudioConnection_F32 patchCord3(sineCos, 1, i2sOut, 1); // Cosine +AudioControlSGTL5000 codec1; + +void setup() { + Serial.begin(9600); // (anything) + delay(1000); + Serial.println("OpenAudio_ArduinoLibrary - Sine-Cosine Stereo"); + // Internally, I16 memory blocks are used. Needs modification, but + // for now, supply 4 for F32 input and 4 for F32 output (shared). + AudioMemory(4); + AudioMemory_F32(20); + codec1.enable(); // MUST be before inputSelect() + codec1.inputSelect(AUDIO_INPUT_LINEIN); + sineCos.amplitude(0.2); sineCos.frequency(600.0f); +} + +void loop() { + Serial.print("Max float memory = "); + Serial.println(AudioStream_F32::f32_memory_used_max); + if(peakL.available()) Serial.print(peakL.read(), 6); + Serial.print(" <-L R-> "); + if(peakR.available()) Serial.println(peakR.read(), 6); + delay(500); +} diff --git a/input_i2s_f32.cpp b/input_i2s_f32.cpp new file mode 100644 index 0000000..96fd170 --- /dev/null +++ b/input_i2s_f32.cpp @@ -0,0 +1,1316 @@ +/* + * input_i2s_f32.cpp + * + * 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. + */ + + /* + * 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. + */ + +#include //do we really need this? (Chip: 2020-10-31) +// #include "input_i2s_f32.h" <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +// #include "output_i2s_f32.h" <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#include "input_i2s_OA_f32.h" +#include "output_i2s_OA_F32.h" + +#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. +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; +bool AudioInputI2S_F32::update_responsibility = false; +DMAChannel AudioInputI2S_F32::dma(false); + +int AudioInputI2S_F32::flag_out_of_memory = 0; +unsigned long AudioInputI2S_F32::update_counter = 0; + +float AudioInputI2S_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE; +int AudioInputI2S_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES; + + +//#for 16-bit transfers +#define I2S_BUFFER_TO_USE_BYTES (AudioOutputI2S_F32::audio_block_samples*sizeof(i2s_rx_buffer[0])) + +//#for 32-bit transfers +//#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; + begin(transferUsing32bit); +} + +void AudioInputI2S_F32::begin(bool transferUsing32bit) { + dma.begin(true); // Allocate the DMA channel first + + AudioOutputI2S_F32::sample_rate_Hz = sample_rate_Hz; //these were given in the AudioSettings in the contructor + AudioOutputI2S_F32::audio_block_samples = audio_block_samples;//these were given in the AudioSettings in the contructor + + //block_left_1st = NULL; + //block_right_1st = NULL; + + // TODO: should we set & clear the I2S_RCSR_SR bit here? + AudioOutputI2S_F32::config_i2s(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->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + dma.TCD->SLAST = 0; + dma.TCD->DADDR = i2s_rx_buffer; + dma.TCD->DOFF = 2; + //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->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->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); + + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + +#elif defined(__IMXRT1062__) + 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->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + dma.TCD->SLAST = 0; + dma.TCD->DADDR = i2s_rx_buffer; + dma.TCD->DOFF = 2; + //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->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->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); + + I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; +#endif + update_responsibility = update_setup(); + dma.enable(); + dma.attachInterrupt(isr); + + update_counter = 0; +} + +/* void AudioInputI2S_F32::begin(bool transferUsing32bit) { + dma.begin(true); // Allocate the DMA channel first + + AudioOutputI2S_F32::sample_rate_Hz = sample_rate_Hz; //these were given in the AudioSettings in the contructor + AudioOutputI2S_F32::audio_block_samples = audio_block_samples;//these were given in the AudioSettings in the contructor + + //setup I2S parameters + AudioOutputI2S_F32::config_i2s(transferUsing32bit); + + // TODO: should we set & clear the I2S_RCSR_SR bit here? + CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 + + // setup DMA parameters + //if (transferUsing32bit) { + sub_begin_i32(); + //} else { + // sub_begin_i16(); + //} + + // finish DMA setup + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); + update_responsibility = update_setup(); + dma.enable(); + // finish I2S parameters + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + + //if (transferUsing32bit) { + dma.attachInterrupt(isr_32); + //} else { + // dma.attachInterrupt(isr_16); + //} + + update_counter = 0; +} */ + +/* void AudioInputI2S_F32::sub_begin_i16(void) +{ + dma.TCD->SADDR = &I2S0_RDR0; + dma.TCD->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + dma.TCD->SLAST = 0; + dma.TCD->DADDR = i2s_rx_buffer; + dma.TCD->DOFF = 2; + + //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + //dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original + dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES; + //dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; +}; */ + +/* void AudioInputI2S_F32::sub_begin_i32(void) +{ + //let's assume that we'll transfer one sample (left or right) each call. So, it'll transfer 4 bytes (32-bits) + dma.TCD->SADDR = (void *)((uint32_t)&I2S0_RDR0); + dma.TCD->SOFF = 0; //do not increment the source memory pointer + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(DMA_TCD_ATTR_SIZE_32BIT) | DMA_TCD_ATTR_DSIZE(DMA_TCD_ATTR_SIZE_32BIT); + dma.TCD->NBYTES_MLNO = 4; //one sample (32bits = 4bytes). should be 4 or 8? https://forum.pjrc.com/threads/42233-I2S-Input-Question + dma.TCD->SLAST = 0; + dma.TCD->DADDR = i2s_rx_buffer; + dma.TCD->DOFF = 4; //increment one sample (32bits = 4bytes) in the destination memory + + //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original, 16-bit + //dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; //revised WEA 16-bit + //dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer_32) / 4; //original, 32-bit + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; //number of minor loops in a major loop. I2S_BUFFER_TO_USE_BYTES/NBYTES_MLNO? ...should be 4 or 8? https://forum.pjrc.com/threads/42233-I2S-Input-Question + + //dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original, 16-bit + //dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES;//revised WEA 16-bit + //dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer_32);//original, 32-bit + dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES;//revised WEA 32-bit + + //dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original, 16-bit + //dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2;//revised WEA 16-bit + //dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer_32) / 4; //original, 32-bit + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; //number of minor loops in a major loop. I2S_BUFFER_TO_USE_BYTES/NBYTES_MLNO?..should be 4 or 8? https://forum.pjrc.com/threads/42233-I2S-Input-Question + + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; +}; */ + +/* void AudioInputI2S_F32::isr_16(void) +{ + uint32_t daddr, offset; + const int16_t *src, *end; + int16_t *dest_left, *dest_right; + audio_block_t *left, *right; + //digitalWriteFast(3, HIGH); +#if defined(KINETISK) + daddr = (uint32_t)(dma.TCD->DADDR); +#endif + dma.clearInterrupt(); + //if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { + if (daddr < (uint32_t)i2s_rx_buffer + I2S_BUFFER_TO_USE_BYTES / 2) { + + // DMA is receiving to the first half of the buffer + // need to remove data from the second half + //src = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original + //end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; //original + src = (int16_t *)&i2s_rx_buffer[audio_block_samples/2]; + end = (int16_t *)&i2s_rx_buffer[audio_block_samples]; + 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]; + //end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original + end = (int16_t *)&i2s_rx_buffer[audio_block_samples/2]; + } + left = AudioInputI2S_F32::block_left; + right = AudioInputI2S_F32::block_right; + if (left != NULL && right != NULL) { + offset = AudioInputI2S_F32::block_offset; + //if (offset <= AUDIO_BLOCK_SAMPLES/2) { //original + if (offset <= ((uint32_t) audio_block_samples/2)) { + dest_left = &(left->data[offset]); + dest_right = &(right->data[offset]); + //AudioInputI2S_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; //original + AudioInputI2S_F32::block_offset = offset + audio_block_samples/2; + do { + //n = *src++; + // *dest_left++ = (int16_t)n; + // *dest_right++ = (int16_t)(n >> 16); + *dest_left++ = *src++; + *dest_right++ = *src++; + } while (src < end); + } + } + //digitalWriteFast(3, LOW); +} */ + + +void AudioInputI2S_F32::isr(void) +{ + uint32_t daddr, offset; + const int16_t *src, *end; + //int16_t *dest_left, *dest_right; + //audio_block_t *left, *right; + float32_t *dest_left_f32, *dest_right_f32; + audio_block_f32_t *left_f32, *right_f32; + +#if defined(KINETISK) || defined(__IMXRT1062__) + daddr = (uint32_t)(dma.TCD->DADDR); +#endif + dma.clearInterrupt(); + //Serial.println("isr"); + + //if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { //original Teensy Audio Library + if (daddr < (uint32_t)i2s_rx_buffer + I2S_BUFFER_TO_USE_BYTES / 2) { + // DMA is receiving to the first half of the buffer + // 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]; + 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]; + //end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio Library + end = (int16_t *)&i2s_rx_buffer[audio_block_samples/2]; + } + left_f32 = AudioInputI2S_F32::block_left_f32; + right_f32 = AudioInputI2S_F32::block_right_f32; + if (left_f32 != NULL && right_f32 != NULL) { + offset = AudioInputI2S_F32::block_offset; + //if (offset <= (uint32_t)(AUDIO_BLOCK_SAMPLES/2)) { //original Teensy Audio Library + if (offset <= ((uint32_t) audio_block_samples/2)) { + dest_left_f32 = &(left_f32->data[offset]); + dest_right_f32 = &(right_f32->data[offset]); + //AudioInputI2S_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; //original Teensy Audio Library + AudioInputI2S_F32::block_offset = offset + audio_block_samples/2; + do { + //Serial.println(*src); + //n = *src++; + //*dest_left++ = (int16_t)n; + //*dest_right++ = (int16_t)(n >> 16); + *dest_left_f32++ = (float32_t) *src++; + *dest_right_f32++ = (float32_t) *src++; + } while (src < end); + } + } +} + + + +/* void AudioInputI2S_F32::isr_32(void) +{ + static bool flag_beenSuccessfullOnce = false; + uint32_t daddr, offset; + const int32_t *src_i32, *end_i32; + //int16_t *dest_left, *dest_right; + float32_t *dest_left_f32, *dest_right_f32; + audio_block_f32_t *left_f32, *right_f32; + daddr = (uint32_t)(dma.TCD->DADDR); + + dma.clearInterrupt(); + //if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { + if (daddr < (uint32_t)i2s_rx_buffer + I2S_BUFFER_TO_USE_BYTES / 2) { + + // DMA is receiving to the first half of the buffer + // need to remove data from the second half + //src = (int32_t *)&i2s_rx_buffer_32[AUDIO_BLOCK_SAMPLES]; + //end = (int32_t *)&i2s_rx_buffer_32[AUDIO_BLOCK_SAMPLES*2]; + src_i32 = (int32_t *)&i2s_rx_buffer[audio_block_samples]; //WEA revised + end_i32 = (int32_t *)&i2s_rx_buffer[audio_block_samples*2]; //WEA revised + 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 = (int32_t *)&i2s_rx_buffer_32[0]; + //end = (int32_t *)&i2s_rx_buffer_32[AUDIO_BLOCK_SAMPLES]; + src_i32 = (int32_t *)&i2s_rx_buffer[0]; + end_i32 = (int32_t *)&i2s_rx_buffer[audio_block_samples]; + } + + // OLD COMMENT: extract 16 but from 32 bit I2S buffer but shift to right first + // OLD COMMENT: there will be two buffers with each having "AUDIO_BLOCK_SAMPLES" samples + left_f32 = AudioInputI2S_F32::block_left_f32; + right_f32 = AudioInputI2S_F32::block_right_f32; + if ((left_f32 != NULL) && (right_f32 != NULL)) { + offset = AudioInputI2S_F32::block_offset; + //if (offset <= AUDIO_BLOCK_SAMPLES/2) { //original + if (offset <= ((uint32_t) audio_block_samples/2)) { + dest_left_f32 = &(left_f32->data[offset]); + dest_right_f32 = &(right_f32->data[offset]); + //AudioInputI2S_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; //original + AudioInputI2S_F32::block_offset = offset + audio_block_samples/2; + do { + //n = *src++; + // *dest_left++ = (int16_t)n; + // *dest_right++ = (int16_t)(n >> 16); + *dest_left_f32++ = (float32_t) *src_i32++; + *dest_right_f32++ = (float32_t) *src_i32++; + } while (src_i32 < end_i32); + } + flag_beenSuccessfullOnce = true; + } else { + if (flag_beenSuccessfullOnce) { + //but we were not successful this time + Serial.println("Input I2S: isr_32: WARNING!!! Null memory block."); + } + } +} */ + +#define I16_TO_F32_NORM_FACTOR (3.051850947599719e-05) //which is 1/32767 +void AudioInputI2S_F32::scale_i16_to_f32( float32_t *p_i16, float32_t *p_f32, int len) { + for (int i=0; i= AUDIO_BLOCK_SAMPLES) { //original + 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 + + // but, first, convert them to F32 + audio_block_f32_t *out_left_f32=NULL, *out_right_f32=NULL; + out_left_f32 = AudioStream_F32::allocate_f32(); + if (out_left_f32 != NULL) { + out_right_f32 = AudioStream_F32::allocate_f32(); + if (out_right_f32 == NULL) { + flag_out_of_memory = 2; + AudioStream_F32::release(out_left_f32); + out_left_f32 = NULL; + } + } else { + flag_out_of_memory = 2; + } + if (out_left_f32 != NULL) { + //convert int16 to float 32 + scale_i16_to_f32(out_left->data, out_left_f32->data, audio_block_samples); + scale_i16_to_f32(out_right->data, out_right_f32->data, audio_block_samples); + + //prepare to transmit + update_counter++; + out_left_f32->id = update_counter; + out_right_f32->id = update_counter; + + //transmit the f32 data! + AudioStream_F32::transmit(out_left_f32,0); + AudioStream_F32::transmit(out_right_f32,1); + AudioStream_F32::release(out_left_f32); + AudioStream_F32::release(out_right_f32); + } + AudioStream::release(out_left); + AudioStream::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(); + AudioStream::release(new_left); + AudioStream::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(); + } +} + */ + + void AudioInputI2S_F32::update_1chan(int chan, audio_block_f32_t *&out_f32) { + 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); + + //prepare to transmit by setting the update_counter (which helps tell if data is skipped or out-of-order) + out_f32->id = update_counter; + + //transmit the f32 data! + AudioStream_F32::transmit(out_f32,chan); + + //release the memory blocks + AudioStream_F32::release(out_f32); +} + +void AudioInputI2S_F32::update(void) +{ + static bool flag_beenSuccessfullOnce = false; + audio_block_f32_t *new_left=NULL, *new_right=NULL, *out_left=NULL, *out_right=NULL; + + new_left = AudioStream_F32::allocate_f32(); + new_right = AudioStream_F32::allocate_f32(); + if ((!new_left) || (!new_right)) { + //ran out of memory. Clear and return! + if (new_left) AudioStream_F32::release(new_left); + if (new_right) AudioStream_F32::release(new_right); + new_left = NULL; new_right = NULL; + flag_out_of_memory = 1; + if (flag_beenSuccessfullOnce) Serial.println("Input_I2S_F32: update(): WARNING!!! Out of Memory."); + } else { + flag_beenSuccessfullOnce = true; + } + + __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_f32; + block_left_f32 = new_left; + out_right = block_right_f32; + block_right_f32 = new_right; + block_offset = 0; + __enable_irq(); + + //update_counter++; //I chose to update it in the ISR instead. + update_1chan(0,out_left); //uses audio_block_samples and update_counter + update_1chan(1,out_right); //uses audio_block_samples and update_counter + + + } else if (new_left != NULL) { + // the DMA didn't fill blocks, but we allocated blocks + if (block_left_f32 == NULL) { + // the DMA doesn't have any blocks to fill, so + // give it the ones we just allocated + block_left_f32 = new_left; + block_right_f32 = new_right; + block_offset = 0; + __enable_irq(); + } else { + // the DMA already has blocks, doesn't need these + __enable_irq(); + AudioStream_F32::release(new_left); + AudioStream_F32::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(); + } +} + +/******************************************************************/ + + +void AudioInputI2Sslave_F32::begin(void) +{ + dma.begin(true); // Allocate the DMA channel first + + //block_left_1st = NULL; + //block_right_1st = NULL; + + AudioOutputI2Sslave_F32::config_i2s(); +#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->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + 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->DLASTSGA = -sizeof(i2s_rx_buffer); + dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); + update_responsibility = update_setup(); + dma.enable(); + + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + dma.attachInterrupt(isr); +#endif + +} + + + +/* +void AudioInputI2Sslave::begin(void) +{ + dma.begin(true); // Allocate the DMA channel first + //block_left_1st = NULL; + //block_right_1st = NULL; + AudioOutputI2Sslave::config_i2s(); + CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 +#if defined(KINETISK) + dma.TCD->SADDR = &I2S0_RDR0; + dma.TCD->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + 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->DLASTSGA = -sizeof(i2s_rx_buffer); + dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; +#endif + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); + update_responsibility = update_setup(); + dma.enable(); + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + dma.attachInterrupt(isr); +} +*/ +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +#if 0 +/* + * input_i2s_OA_f32.cpp + * Input I2S audio blocks from a ADC (Codec) device, such as SGTL-5000. + * Includes conversion to 32-bit floating point with full scale from + * -1.0 to 1.0. Supports: + * 16-bit I2S data + * Variable sample rate (via AudioSettings and outputi2s_OA_f32). + * Variable block size (recommend 128, 64 or 32.) see AudioSettings. + * Master or slave I2S data clocking. + * + * This routine is strongly based on the I16 Teensy Audio Library. + * Modifications by Chip Audette, + + * 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. + */ +#include //<<<<<<<<<< +#include "input_i2s_OA_f32.h" +#include "output_i2s_OA_F32.h" +#include + +// DMAMEM static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; +DMAMEM __attribute__((aligned(32))) static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES];//<<<<<<<< +audio_block_t * AudioInputI2S_OA_F32::block_left = NULL; +audio_block_t * AudioInputI2S_OA_F32::block_right = NULL; +uint16_t AudioInputI2S_OA_F32::block_offset = 0; +bool AudioInputI2S_OA_F32::update_responsibility = false; +DMAChannel AudioInputI2S_OA_F32::dma(false); + +// This will eventually be integrated to AudioSettings making them settable +float AudioInputI2S_OA_F32::sample_rate_Hz_i = AUDIO_SAMPLE_RATE; +int AudioInputI2S_OA_F32::audio_block_samples_i = AUDIO_BLOCK_SAMPLES; + +#define I2S_BUFFER_TO_USE_BYTES (AudioInputI2S_OA_F32::audio_block_samples_i*sizeof(i2s_rx_buffer[0])) + +void AudioInputI2S_OA_F32::begin(void) { + dma.begin(true); // Allocate the DMA channel first + //block_left_1st = NULL; + //block_right_1st = NULL; + + // TODO: should we set & clear the I2S_RCSR_SR bit here? + //AudioInputI2S_OA_F32::sample_rate_Hz = sample_rate_Hz; <<<<<<<<SADDR = (void *)((uint32_t)&I2S0_RDR0 + 2); + dma.TCD->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + 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->DLASTSGA = -sizeof(i2s_rx_buffer); + dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); + + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + +#elif defined(__IMXRT1062__) + 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->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + 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->DLASTSGA = -sizeof(i2s_rx_buffer); + dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); + + I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; +#endif +// **************END TEENSY AUDIO ********** + + +/* +// ///////////////////NEW NEW IS THIS 32 bit??? ////////////// +#if defined(KINETISK) + CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 + dma.TCD->SADDR = &I2S0_RDR0; + dma.TCD->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + dma.TCD->SLAST = 0; + dma.TCD->DADDR = i2s_rx_buffer; + dma.TCD->DOFF = 2; + //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + //dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original + dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES; + //dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + +#elif defined(__IMXRT1062__) + 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->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + dma.TCD->SLAST = 0; + dma.TCD->DADDR = i2s_rx_buffer; + dma.TCD->DOFF = 2; + + //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + //dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original + dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES; + //dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + + + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + //dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); //was + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); //revise + +//new + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + +#elif defined(__IMXRT1062__) + 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->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + 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->DLASTSGA = -sizeof(i2s_rx_buffer); + dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); +//new + + I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + #endif +// ///////////////END NEW END NEW //////////// +*/ + + update_responsibility = update_setup(); + dma.enable(); + dma.attachInterrupt(isr); +}; + +void AudioInputI2S_OA_F32::isr(void) +{ + uint32_t daddr, offset; + const int16_t *src, *end; + int16_t *dest_left, *dest_right; + audio_block_t *left, *right; + + //digitalWriteFast(3, HIGH); + + + +// FROM TEENSY AUDIO I16 ***** +#if defined(KINETISK) || defined(__IMXRT1062__) + daddr = (uint32_t)(dma.TCD->DADDR); + dma.clearInterrupt(); + //Serial.println("isr"); + + if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { + // DMA is receiving to the first half of the buffer + // need to remove data from the second half + src = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; + end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; + if (AudioInputI2S_OA_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]; + end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; + } + left = AudioInputI2S_OA_F32::block_left; + right = AudioInputI2S_OA_F32::block_right; + if (left != NULL && right != NULL) { + offset = AudioInputI2S_OA_F32::block_offset; + if (offset <= AUDIO_BLOCK_SAMPLES/2) { + dest_left = &(left->data[offset]); + dest_right = &(right->data[offset]); + AudioInputI2S_OA_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; + arm_dcache_delete((void*)src, sizeof(i2s_rx_buffer) / 2); + do { + *dest_left++ = *src++; + *dest_right++ = *src++; + } while (src < end); + } + } +#endif +// END FROM TEENSY AUDIO I16 ***** + + + +// NEW NEW 32-bit?? NEW ****************** +#if 0 +/* +#if defined(KINETISK) || defined(__IMXRT1062__) + daddr = (uint32_t)(dma.TCD->DADDR); + dma.clearInterrupt(); + + //if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { + if (daddr < (uint32_t)i2s_rx_buffer + I2S_BUFFER_TO_USE_BYTES / 2) { //<<<<<data[offset]); + dest_right = &(right->data[offset]); + //AudioInputI2S_OA_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; //original + AudioInputI2S_OA_F32::block_offset = offset + audio_block_samples_i/2; + do { + //n = *src++; + //*dest_left++ = (int16_t)n; + //*dest_right++ = (int16_t)(n >> 16); + *dest_left++ = *src++; + *dest_right++ = *src++; + } while (src < end); + } + } + //digitalWriteFast(3, LOW); +#endif +*/ +#endif + +} + +#define I16_TO_F32_NORM_FACTOR (3.05175781E-05) //which is 1/32768 +void AudioInputI2S_OA_F32::convert_i16_to_f32( int16_t *p_i16, float32_t *p_f32, int len) { + for (int i=0; i= AUDIO_BLOCK_SAMPLES) { //original + if (block_offset >= audio_block_samples_i) { + // 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 + + // but, first, convert them to F32 + audio_block_f32_t *out_left_f32=NULL, *out_right_f32=NULL; + out_left_f32 = AudioStream_F32::allocate_f32(); + if (out_left_f32 != NULL) { + out_right_f32 = AudioStream_F32::allocate_f32(); + if (out_right_f32 == NULL) { + AudioStream_F32::release(out_left_f32); + out_left_f32 = NULL; + } + } + if (out_left_f32 != NULL) { + //convert int16 to float 32 + convert_i16_to_f32(out_left->data, out_left_f32->data, audio_block_samples_i); + convert_i16_to_f32(out_right->data, out_right_f32->data, audio_block_samples_i); + + //transmit the f32 data! + AudioStream_F32::transmit(out_left_f32,0); + AudioStream_F32::transmit(out_right_f32,1); + AudioStream_F32::release(out_left_f32); + AudioStream_F32::release(out_right_f32); + } + AudioStream::release(out_left); + AudioStream::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(); + AudioStream::release(new_left); + AudioStream::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(); + } +} + + + + + +#if 0 +// @@@@@@@@@@@@@@@@@ +/* Monday night +input_i2s_f32X.cpp +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. + */ + +#include "input_i2s_OA_f32.h" +#include "output_i2s_OA_F32.h" +#include + +DMAMEM static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; +audio_block_t * AudioInputI2S_OA_F32::block_left = NULL; +audio_block_t * AudioInputI2S_OA_F32::block_right = NULL; +uint16_t AudioInputI2S_OA_F32::block_offset = 0; +bool AudioInputI2S_OA_F32::update_responsibility = false; +DMAChannel AudioInputI2S_OA_F32::dma(false); + +// This will eventually be integrated to AudioSettings +float AudioInputI2S_OA_F32::sample_rate_Hz_i = AUDIO_SAMPLE_RATE; +int AudioInputI2S_OA_F32::audio_block_samples_i = AUDIO_BLOCK_SAMPLES; + +#define I2S_BUFFER_TO_USE_BYTES (AudioInputI2S_OA_F32::audio_block_samples_i*sizeof(i2s_rx_buffer[0])) + +void AudioInputI2S_OA_F32::begin(void) { + dma.begin(true); // Allocate the DMA channel first + //block_left_1st = NULL; + //block_right_1st = NULL; + + // TODO: should we set & clear the I2S_RCSR_SR bit here? + //AudioInputI2S_OA_F32::sample_rate_Hz = sample_rate_Hz; + //AudioInputI2S_OA_F32::audio_block_samples = audio_block_samples; + AudioOutputI2S_OA_F32::config_i2s(); + + + //setup I2S parameters + //AudioOutputI2S_F32::config_i2s(transferUsing32bit); + + + + +#if defined(KINETISK) + CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 + dma.TCD->SADDR = &I2S0_RDR0; + dma.TCD->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + dma.TCD->SLAST = 0; + dma.TCD->DADDR = i2s_rx_buffer; + dma.TCD->DOFF = 2; + //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + //dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original + dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES; + //dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + +#elif defined(__IMXRT1062__) + 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->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + dma.TCD->SLAST = 0; + dma.TCD->DADDR = i2s_rx_buffer; + dma.TCD->DOFF = 2; + + //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + //dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original + dma.TCD->DLASTSGA = -I2S_BUFFER_TO_USE_BYTES; + //dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + + + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + //dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); //was + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); //revise + +//new + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + +#elif defined(__IMXRT1062__) + 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->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + 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->DLASTSGA = -sizeof(i2s_rx_buffer); + dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX); +//new + + + + I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + #endif + + update_responsibility = update_setup(); + dma.enable(); + dma.attachInterrupt(isr); +}; + +void AudioInputI2S_OA_F32::isr(void) +{ + uint32_t daddr, offset; + const int16_t *src, *end; + int16_t *dest_left, *dest_right; + audio_block_t *left, *right; + + //digitalWriteFast(3, HIGH); +#if defined(KINETISK) || defined(__IMXRT1062__) + daddr = (uint32_t)(dma.TCD->DADDR); +#endif + dma.clearInterrupt(); + + //if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { + if (daddr < (uint32_t)i2s_rx_buffer + I2S_BUFFER_TO_USE_BYTES / 2) { + + // DMA is receiving to the first half of the buffer + // need to remove data from the second half + //src = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original + //end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; //original + src = (int16_t *)&i2s_rx_buffer[audio_block_samples_i/2]; + end = (int16_t *)&i2s_rx_buffer[audio_block_samples_i]; + if (AudioInputI2S_OA_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]; + //end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original + end = (int16_t *)&i2s_rx_buffer[audio_block_samples_i/2]; + } + left = AudioInputI2S_OA_F32::block_left; + right = AudioInputI2S_OA_F32::block_right; + if (left != NULL && right != NULL) { + offset = AudioInputI2S_OA_F32::block_offset; + //if (offset <= AUDIO_BLOCK_SAMPLES/2) { //original + if (offset <= ((uint32_t) audio_block_samples_i/2)) { + dest_left = &(left->data[offset]); + dest_right = &(right->data[offset]); + //AudioInputI2S_OA_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; //original + AudioInputI2S_OA_F32::block_offset = offset + audio_block_samples_i/2; + do { + //n = *src++; + //*dest_left++ = (int16_t)n; + //*dest_right++ = (int16_t)(n >> 16); + *dest_left++ = *src++; + *dest_right++ = *src++; + } while (src < end); + } + } + //digitalWriteFast(3, LOW); +} + +#define I16_TO_F32_NORM_FACTOR (3.05175781E-05) //which is 1/32768 +void AudioInputI2S_OA_F32::convert_i16_to_f32( int16_t *p_i16, float32_t *p_f32, int len) { + for (int i=0; i= AUDIO_BLOCK_SAMPLES) { //original + if (block_offset >= audio_block_samples_i) { + // 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 + + // but, first, convert them to F32 + audio_block_f32_t *out_left_f32=NULL, *out_right_f32=NULL; + out_left_f32 = AudioStream_F32::allocate_f32(); + if (out_left_f32 != NULL) { + out_right_f32 = AudioStream_F32::allocate_f32(); + if (out_right_f32 == NULL) { + AudioStream_F32::release(out_left_f32); + out_left_f32 = NULL; + } + } + if (out_left_f32 != NULL) { + //convert int16 to float 32 + convert_i16_to_f32(out_left->data, out_left_f32->data, audio_block_samples_i); + convert_i16_to_f32(out_right->data, out_right_f32->data, audio_block_samples_i); + + //transmit the f32 data! + AudioStream_F32::transmit(out_left_f32,0); + AudioStream_F32::transmit(out_right_f32,1); + AudioStream_F32::release(out_left_f32); + AudioStream_F32::release(out_right_f32); + } + AudioStream::release(out_left); + AudioStream::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(); + AudioStream::release(new_left); + AudioStream::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(); + } +} + + +/******************************************************************/ + +/* +void AudioInputI2Sslave::begin(void) +{ + dma.begin(true); // Allocate the DMA channel first + //block_left_1st = NULL; + //block_right_1st = NULL; + AudioOutputI2Sslave::config_i2s(); + CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0 +#if defined(KINETISK) + dma.TCD->SADDR = &I2S0_RDR0; + dma.TCD->SOFF = 0; + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); + dma.TCD->NBYTES_MLNO = 2; + 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->DLASTSGA = -sizeof(i2s_rx_buffer); + dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; +#endif + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX); + update_responsibility = update_setup(); + dma.enable(); + I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; + I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX + dma.attachInterrupt(isr); +} +*/ +//@@@@@@@@@@@@@@ +#endif + +#endif diff --git a/input_i2s_f32.h b/input_i2s_f32.h new file mode 100644 index 0000000..70cb624 --- /dev/null +++ b/input_i2s_f32.h @@ -0,0 +1,167 @@ +/* + * ***** input_i2s_f32.h ****** + * + * 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. + */ + + /* + * 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 _input_i2s_f32_h_ +#define _input_i2s_f32_h_ + +#include +#include +#include "AudioStream_F32.h" +#include "AudioStream.h" //Do we really need this?? (Chip, 2020-10-31) +#include "DMAChannel.h" + +class AudioInputI2S_F32 : public AudioStream_F32 +{ +//GUI: inputs:0, outputs:2 //this line used for automatic generation of GUI nodes +public: + AudioInputI2S_F32(void) : AudioStream_F32(0, NULL) { begin(); } //uses default AUDIO_SAMPLE_RATE and BLOCK_SIZE_SAMPLES from AudioStream.h + AudioInputI2S_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL) { + sample_rate_Hz = settings.sample_rate_Hz; + audio_block_samples = settings.audio_block_samples; + begin(); + } + + virtual void update(void); + static void scale_i16_to_f32( float32_t *p_i16, float32_t *p_f32, int len) ; + static void scale_i24_to_f32( float32_t *p_i24, float32_t *p_f32, int len) ; + static void scale_i32_to_f32( float32_t *p_i32, float32_t *p_f32, int len); + void begin(void); + void begin(bool); + void sub_begin_i32(void); + //void sub_begin_i16(void); + int get_isOutOfMemory(void) { return flag_out_of_memory; } + void clear_isOutOfMemory(void) { flag_out_of_memory = 0; } + //friend class AudioOutputI2S_F32; +protected: + AudioInputI2S_F32(int dummy): AudioStream_F32(0, NULL) {} // to be used only inside AudioInputI2Sslave !! + static bool update_responsibility; + static DMAChannel dma; + static void isr_32(void); + static void isr(void); + virtual void update_1chan(int, audio_block_f32_t *&); +private: + static audio_block_f32_t *block_left_f32; + static audio_block_f32_t *block_right_f32; + static float sample_rate_Hz; + static int audio_block_samples; + static uint16_t block_offset; + static int flag_out_of_memory; + static unsigned long update_counter; +}; + +class AudioInputI2Sslave_F32 : public AudioInputI2S_F32 +{ +public: + AudioInputI2Sslave_F32(void) : AudioInputI2S_F32(0) { begin(); } + void begin(void); + friend void dma_ch1_isr(void); +}; +#endif +/////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////// +#if 0 +/* 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 _input_i2s_OA_f32_h_ +#define _input_i2s_OA_f32_h_ + +#include "Arduino.h" +#include "AudioStream_F32.h" +#include "AudioStream.h" +#include "DMAChannel.h" +#include "AudioSettings_F32.h" + +class AudioInputI2S_OA_F32 : public AudioStream_F32 +{ +//GUI: inputs:0, outputs:2 //this line used for automatic generation of GUI nodes +public: + AudioInputI2S_OA_F32(void) : AudioStream_F32(0, NULL) { begin(); } //uses default AUDIO_SAMPLE_RATE and BLOCK_SIZE_SAMPLES from AudioStream.h + +// For now, only allow 128 blocks and 44.1kHz +/* AudioInputI2S_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL) { + sample_rate_Hz = settings.sample_rate_Hz; + audio_block_samples = settings.audio_block_samples; + begin(); + } + */ + + virtual void update(void); + static void convert_i16_to_f32( int16_t *p_i16, float32_t *p_f32, int len) ; + void begin(void); + friend class AudioOutputI2S_OA_F32; +protected: + AudioInputI2S_OA_F32(int dummy): AudioStream_F32(0, NULL) {} // to be used only inside AudioInputI2Sslave !! + static bool update_responsibility; + static DMAChannel dma; + static void isr(void); +private: + static audio_block_t *block_left; + static audio_block_t *block_right; + static float sample_rate_Hz_i; + static int audio_block_samples_i; + static uint16_t block_offset; +}; +#endif + + +#endif diff --git a/output_i2s_f32.cpp b/output_i2s_f32.cpp new file mode 100644 index 0000000..810a1fd --- /dev/null +++ b/output_i2s_f32.cpp @@ -0,0 +1,1681 @@ +/* + * ***** output_i2s_f32.cpp ***** + +/* 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. + */ + + /* + * 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. + */ + + + +// #include "output_i2s_f32.h" <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +#include "output_i2s_OA_F32.h" +//#include "input_i2s_f32.h" +//include "memcpy_audio.h" +//#include "memcpy_interleave.h" +#include +#include //to get access to Audio/utlity/imxrt_hw.h...do we really need this??? WEA 2020-10-31 + + /* Comment this out +//taken from Teensy Audio utility/imxrt_hw.h and imxrt_hw.cpp... +#if defined(__IMXRT1062__) + #ifndef imxr_hw_h_ + #define imxr_hw_h_ + #define IMXRT_CACHE_ENABLED 2 // 0=disabled, 1=WT, 2= WB + #include + #include + PROGMEM + void set_audioClock_tympan(int nfact, int32_t nmult, uint32_t ndiv, bool force = false) // sets PLL4 + { + if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return; + CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE + | CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1 + | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact); + CCM_ANALOG_PLL_AUDIO_NUM = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK; + CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK; + + CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL + while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock + + const int div_post_pll = 1; // other values: 2,4 + CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB); + if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB; + if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB; + + CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;//Disable Bypass + } + #endif +#else + //No IMXRT + #define IMXRT_CACHE_ENABLED 0 +#endif +*/ //end of commented block + +//////////// +// +// Changing the sample rate based on changing the I2S bus freuqency (just for Teensy 3.x??) +// +//Here's the function to change the sample rate of the system (via changing the clocking of the I2S bus) +//https://forum.pjrc.com/threads/38753-Discussion-about-a-simple-way-to-change-the-sample-rate?p=121365&viewfull=1#post121365 +// +//And, a post on how to compute the frac and div portions? I haven't checked the code presented in this post: +//https://forum.pjrc.com/threads/38753-Discussion-about-a-simple-way-to-change-the-sample-rate?p=188812&viewfull=1#post188812 +// +//Finally, here is my own Matlab code for computing the mult and div values...(again, just for Teensy 3.x??) +/* + %choose the sample rates that you are hoping to hit + targ_fs_Hz = [2000, 8000, 11025, 16000, 22050, 24000, 32000, 44100, floor(44117.64706) , ... + 48000, 88200, floor(44117.64706 * 2), (37000/256*662), 96000, 176400, floor(44117.64706 * 4), 192000]; + F_PLL = 180e6; %choose the clock rate used for this calculation + PLL_div = 256; + all_n=[];all_d=[]; + for Itarg=1:length(targ_fs_Hz) + if (0) + [best_d,best_n]=rat((F_PLL/PLL_div)/targ_fs_Hz(Itarg)); + else + best_n = 1; best_d = 1; best_err = 1e10; + for n=1:255 + d = [1:4095]; + act_fs_Hz = F_PLL / PLL_div * n ./ d; + [err,I] = min(abs(act_fs_Hz - targ_fs_Hz(Itarg))); + if err < best_err + best_n = n; best_d = d(I); + best_err = err; + end + end + end + all_n(Itarg) = best_n; + all_d(Itarg) = best_d; + disp(['fs = ' num2str(targ_fs_Hz(Itarg)) ', n = ' num2str(best_n) ', d = ' num2str(best_d) ', true = ' num2str(F_PLL/PLL_div * best_n / best_d)]) + end +*/ + + +float AudioOutputI2S_F32::setI2SFreq_T3(const float freq_Hz) { +#if defined(KINETISK) //for Teensy 3.x only! + int freq = (int)(freq_Hz+0.5); + typedef struct { + uint8_t mult; + uint16_t div; + } __attribute__((__packed__)) tmclk; + + const int numfreqs = 17; + const int samplefreqs[numfreqs] = { 2000, 8000, 11025, 16000, 22050, 24000, 32000, 44100, (int)44117.64706 , 48000, 88200, (int)(44117.64706 * 2), (int)(95679.69+0.5), 96000, 176400, (int)(44117.64706 * 4), 192000}; + +#if (F_PLL==16000000) + const tmclk clkArr[numfreqs] = {{4, 125}, {16, 125}, {148, 839}, {32, 125}, {145, 411}, {48, 125}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {124,81}, {192, 125}, {127, 45}, {48, 17}, {255, 83} }; +#elif (F_PLL==72000000) + const tmclk clkArr[numfreqs] = {{832, 1125}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {32, 375}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {248,729}, {128, 375}, {249, 397}, {32, 51}, {185, 271} }; +#elif (F_PLL==96000000) + const tmclk clkArr[numfreqs] = {{2, 375},{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {8, 125}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {62,243},{32, 125}, {151, 321}, {8, 17}, {64, 125} }; +#elif (F_PLL==120000000) + const tmclk clkArr[numfreqs] = {{8, 1875},{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {32, 625}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {119,583}, {128, 625}, {178, 473}, {32, 85}, {145, 354} }; +#elif (F_PLL==144000000) + const tmclk clkArr[numfreqs] = {{4, 1125},{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {16, 375}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {157,923}, {64, 375}, {196, 625}, {16, 51}, {128, 375} }; +#elif (F_PLL==180000000) + const tmclk clkArr[numfreqs] = {{9, 3164}, {46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {64, 1875}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {238,1749}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} }; +#elif (F_PLL==192000000) + const tmclk clkArr[numfreqs] = {{1, 375}, {4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {4, 125}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {31,243}, {16, 125}, {147, 625}, {4, 17}, {32, 125} }; +#elif (F_PLL==216000000) + const tmclk clkArr[numfreqs] = {{8, 3375}, {32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {32, 1125}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {248,2187}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} }; +#elif (F_PLL==240000000) + const tmclk clkArr[numfreqs] = {{4, 1875}, {16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {16, 625}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {119,1166}, {64, 625}, {89, 473}, {16, 85}, {128, 625} }; +#endif + + for (int f = 0; f < numfreqs; f++) { + if ( freq == samplefreqs[f] ) { + while (I2S0_MCR & I2S_MCR_DUF) ; + I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1)); + return (float)(F_PLL / 256 * clkArr[f].mult / clkArr[f].div); + } + } +#endif + return 0.0f; +} + + +audio_block_f32_t * AudioOutputI2S_F32::block_left_1st = NULL; +audio_block_f32_t * AudioOutputI2S_F32::block_right_1st = NULL; +audio_block_f32_t * AudioOutputI2S_F32::block_left_2nd = NULL; +audio_block_f32_t * AudioOutputI2S_F32::block_right_2nd = NULL; +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 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; +int AudioOutputI2S_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES; + +#if defined(__IMXRT1062__) +#include //from Teensy Audio library. For set_audioClock() +#endif + +//#for 16-bit transfers +#define I2S_BUFFER_TO_USE_BYTES (AudioOutputI2S_F32::audio_block_samples*sizeof(i2s_tx_buffer[0])) + +//#for 32-bit transfers +//#define I2S_BUFFER_TO_USE_BYTES (AudioOutputI2S_F32::audio_block_samples*2*sizeof(i2s_tx_buffer[0])) + + +void AudioOutputI2S_F32::begin(void) +{ + bool transferUsing32bit = false; + begin(transferUsing32bit); +} + +void AudioOutputI2S_F32::begin(bool transferUsing32bit) { + + dma.begin(true); // Allocate the DMA channel first + + block_left_1st = NULL; + block_right_1st = NULL; + + AudioOutputI2S_F32::config_i2s(transferUsing32bit, sample_rate_Hz); + +#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);//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->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->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->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 + + 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);//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->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->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(); //newer location of this line in Teensy Audio library + + 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(AudioOutputI2S_F32::isr); + //dma.enable(); //original location of this line in older Tympan_Library + + enabled = 1; + + //AudioInputI2S_F32::begin_guts(); +} + +void AudioOutputI2S_F32::isr(void) +{ +#if defined(KINETISK) || defined(__IMXRT1062__) + int16_t *dest; + audio_block_f32_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) { //original 16-bit + if (saddr < (uint32_t)i2s_tx_buffer + I2S_BUFFER_TO_USE_BYTES / 2) { //are we transmitting the first half or second half of the buffer? + // 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 + 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; + } + + blockL = AudioOutputI2S_F32::block_left_1st; + blockR = AudioOutputI2S_F32::block_right_1st; + offsetL = AudioOutputI2S_F32::block_left_offset; + offsetR = AudioOutputI2S_F32::block_right_offset; + + int16_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++ = 0; + //*d++ = 0; + } + offsetL += audio_block_samples / 2; + offsetR += audio_block_samples / 2; + } 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 + 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 + offsetR += audio_block_samples / 2; + } else { + //memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); + memset(dest,0,audio_block_samples * 2); + return; + } + + + arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 ); + + //if (offsetL < AUDIO_BLOCK_SAMPLES) { //orig Teensy Audio + if (offsetL < (uint16_t)audio_block_samples) { + AudioOutputI2S_F32::block_left_offset = offsetL; + } else { + AudioOutputI2S_F32::block_left_offset = 0; + AudioStream_F32::release(blockL); + AudioOutputI2S_F32::block_left_1st = AudioOutputI2S_F32::block_left_2nd; + AudioOutputI2S_F32::block_left_2nd = NULL; + } + //if (offsetR < AUDIO_BLOCK_SAMPLES) { //orig Teensy Audio + if (offsetR < (uint16_t)audio_block_samples) { + AudioOutputI2S_F32::block_right_offset = offsetR; + } else { + AudioOutputI2S_F32::block_right_offset = 0; + AudioStream_F32::release(blockR); + AudioOutputI2S_F32::block_right_1st = AudioOutputI2S_F32::block_right_2nd; + AudioOutputI2S_F32::block_right_2nd = NULL; + } +#endif +} + + +/* void AudioOutputI2S_F32::begin(bool transferUsing32bit) { + dma.begin(true); // Allocate the DMA channel first + block_left_1st = NULL; + block_right_1st = NULL; + // TODO: should we set & clear the I2S_TCSR_SR bit here? + config_i2s(transferUsing32bit); + CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 + //setup DMA parameters + //if (transferUsing32bit) { + sub_begin_i32(); + //} else { + // sub_begin_i16(); + //} + + dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX); + update_responsibility = update_setup(); + dma.enable(); + I2S0_TCSR = I2S_TCSR_SR; + I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; + dma.attachInterrupt(isr_32); + + // change the I2S frequencies to make the requested sample rate + setI2SFreq(AudioOutputI2S_F32::sample_rate_Hz); + + enabled = 1; + + //AudioInputI2S_F32::begin_guts(); +} */ + +/* void AudioOutputI2S_F32::sub_begin_i16(void) { + 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); //original + dma.TCD->SLAST = -I2S_BUFFER_TO_USE_BYTES; + dma.TCD->DADDR = &I2S0_TDR0; + dma.TCD->DOFF = 0; + //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->DLASTSGA = 0; + //dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 2; + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; +} +void AudioOutputI2S_F32::sub_begin_i32(void) { + dma.TCD->SADDR = i2s_tx_buffer; //here's where to get the data from + + //let's assume that we'll transfer each sample (left or right) independently. So 4-byte (32bit) transfers. + dma.TCD->SOFF = 4; //step forward pointer for source data by 4 bytes (ie, 32 bits) after each read + dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(DMA_TCD_ATTR_SIZE_32BIT) | DMA_TCD_ATTR_DSIZE(DMA_TCD_ATTR_SIZE_32BIT); //each read is 32 bits + dma.TCD->NBYTES_MLNO = 4; //how many bytes to send per minor loop. Do each sample (left or right) independently. So, 4 bytes? Should be 4 or 8? + + //dma.TCD->SLAST = -sizeof(i2s_tx_buffer); //original + dma.TCD->SLAST = -I2S_BUFFER_TO_USE_BYTES; //jump back to beginning of source data when hit the end + dma.TCD->DADDR = &I2S0_TDR0; //destination of DMA transfers + dma.TCD->DOFF = 0; //do not increment the destination pointer + //dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original + dma.TCD->CITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; //number of minor loops in a major loop. I2S_BUFFER_TO_USE_BYTES/NBYTES_MLNO? Should be 4 or 8? + dma.TCD->DLASTSGA = 0; + //dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //original + dma.TCD->BITER_ELINKNO = I2S_BUFFER_TO_USE_BYTES / 4; //number of minor loops in a major loop. I2S_BUFFER_TO_USE_BYTES/NBYTES_MLNO? should be 4 or 8? + dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; +} + */ + + +/* void AudioOutputI2S_F32::isr_16(void) +{ +#if defined(KINETISK) + 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) { //original + if (saddr < (uint32_t)i2s_tx_buffer + I2S_BUFFER_TO_USE_BYTES / 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]; //original + dest = (int16_t *)&i2s_tx_buffer[audio_block_samples/2]; + 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; + } + blockL = AudioOutputI2S_F32::block_left_1st; + blockR = AudioOutputI2S_F32::block_right_1st; + offsetL = AudioOutputI2S_F32::block_left_offset; + offsetR = AudioOutputI2S_F32::block_right_offset; + + int16_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); + int16_t *pL = blockL->data + offsetL; + int16_t *pR = blockR->data + offsetR; + for (int i=0; i < audio_block_samples/2; i++) { *d++ = *pL++; *d++ = *pR++; } //interleave + offsetL += audio_block_samples / 2; + offsetR += audio_block_samples / 2; + } else if (blockL) { + //memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR); + int16_t *pL = blockL->data + offsetL; + for (int i=0; i < audio_block_samples / 2 * 2; i+=2) { *(d+i) = *pL++; } //interleave + offsetL += audio_block_samples / 2; + } else if (blockR) { + int16_t *pR = blockR->data + offsetR; + for (int i=0; i < audio_block_samples /2 * 2; i+=2) { *(d+i) = *pR++; } //interleave + offsetR += audio_block_samples / 2; + } else { + //memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); + memset(dest,0,audio_block_samples * 2); + return; + } + //if (offsetL < AUDIO_BLOCK_SAMPLES) { //original + if (offsetL < (uint16_t)audio_block_samples) { + AudioOutputI2S_F32::block_left_offset = offsetL; + } else { + AudioOutputI2S_F32::block_left_offset = 0; + AudioStream::release(blockL); + AudioOutputI2S_F32::block_left_1st = AudioOutputI2S_F32::block_left_2nd; + AudioOutputI2S_F32::block_left_2nd = NULL; + } + //if (offsetR < AUDIO_BLOCK_SAMPLES) { + if (offsetR < (uint16_t)audio_block_samples) { + AudioOutputI2S_F32::block_right_offset = offsetR; + } else { + AudioOutputI2S_F32::block_right_offset = 0; + AudioStream::release(blockR); + AudioOutputI2S_F32::block_right_1st = AudioOutputI2S_F32::block_right_2nd; + AudioOutputI2S_F32::block_right_2nd = NULL; + } +#else + 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_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; + end = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2]; + } + block = AudioOutputI2S_F32::block_left_1st; + if (block) { + offset = AudioOutputI2S_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_F32::block_left_offset = offset; + } else { + AudioOutputI2S_F32::block_left_offset = 0; + AudioStream::release(block); + AudioOutputI2S_F32::block_left_1st = AudioOutputI2S_F32::block_left_2nd; + AudioOutputI2S_F32::block_left_2nd = NULL; + } + } else { + do { + *dest = 0; + dest += 2; + } while (dest < end); + } + dest -= AUDIO_BLOCK_SAMPLES - 1; + block = AudioOutputI2S_F32::block_right_1st; + if (block) { + offset = AudioOutputI2S_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_F32::block_right_offset = offset; + } else { + AudioOutputI2S_F32::block_right_offset = 0; + AudioStream::release(block); + AudioOutputI2S_F32::block_right_1st = AudioOutputI2S_F32::block_right_2nd; + AudioOutputI2S_F32::block_right_2nd = NULL; + } + } else { + do { + *dest = 0; + dest += 2; + } while (dest < end); + } +#endif +} */ + +/* void AudioOutputI2S_F32::isr_32(void) //should be called every half of an audio block +{ + int32_t *dest; //int32 is the data type being sent to the audio codec + audio_block_f32_t *blockL, *blockR; + uint32_t saddr; + uint32_t offsetL, offsetR; + + saddr = (uint32_t)(dma.TCD->SADDR); + dma.clearInterrupt(); + //if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) { //original 16-bit + if (saddr < (uint32_t)i2s_tx_buffer + I2S_BUFFER_TO_USE_BYTES / 2) { //are we transmitting the first half or second half of the buffer? + // 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, half-way through buffer (buffer is 32-bit elements filled with 16-bit stereo samples) + dest = (int32_t *)&i2s_tx_buffer[2*(audio_block_samples/2)]; //half-way through the buffer..remember, buffer is 32-bit elements filled with 32-bit stereo 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 = (int32_t *)i2s_tx_buffer; //beginning of the buffer + } + blockL = AudioOutputI2S_F32::block_left_1st; + blockR = AudioOutputI2S_F32::block_right_1st; + offsetL = AudioOutputI2S_F32::block_left_offset; + offsetR = AudioOutputI2S_F32::block_right_offset; + + 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++) { //loop over half of the audio block (this routine gets called every half an audio block) + *d++ = (int32_t) (*pL++); + *d++ = (int32_t) (*pR++); //cast and interleave + } + offsetL += (audio_block_samples / 2); + offsetR += (audio_block_samples / 2); + } 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; i++) { + *d++ = (int32_t) *pL++; //cast and interleave + *d++ = 0; + } + offsetL += (audio_block_samples / 2); + } else if (blockR) { + float32_t *pR = blockR->data + offsetR; + for (int i=0; i < audio_block_samples /2; i++) { + *d++ = 0; + *d++ = (int32_t) *pR++; //cast and interleave + } + offsetR += (audio_block_samples / 2); + } else { + //memset(dest,0,AUDIO_BLOCK_SAMPLES * 2); //half buffer (AUDIO_BLOCK_SAMPLES/2), 16-bits per sample (AUDIO_BLOCK_SAMPLES/2*2), stereo (AUDIO_BLOCK_SAMPLES/2*2*2) + //memset(dest,0,audio_block_samples * 2 * 4 / 2);//half buffer (AUDIO_BLOCK_SAMPLES/2), 32-bits per sample (AUDIO_BLOCK_SAMPLES/2*4), stereo (AUDIO_BLOCK_SAMPLES/2*4*2) + for (int i=0; i < audio_block_samples/2; i++) { //loop over half of the audio block (this routine gets called every half an audio block) + *d++ = (int32_t) 0; + *d++ = (int32_t) 0; + } + return; + } + //if (offsetL < AUDIO_BLOCK_SAMPLES) { //original + if (offsetL < (uint16_t)audio_block_samples) { + AudioOutputI2S_F32::block_left_offset = offsetL; + } else { + AudioOutputI2S_F32::block_left_offset = 0; + AudioStream_F32::release(blockL); + AudioOutputI2S_F32::block_left_1st = AudioOutputI2S_F32::block_left_2nd; + AudioOutputI2S_F32::block_left_2nd = NULL; + } + //if (offsetR < AUDIO_BLOCK_SAMPLES) { + if (offsetR < (uint16_t)audio_block_samples) { + AudioOutputI2S_F32::block_right_offset = offsetR; + } else { + AudioOutputI2S_F32::block_right_offset = 0; + AudioStream_F32::release(blockR); + AudioOutputI2S_F32::block_right_1st = AudioOutputI2S_F32::block_right_2nd; + AudioOutputI2S_F32::block_right_2nd = NULL; + } +} + */ +#define F32_TO_I16_NORM_FACTOR (32767) //which is 2^15-1 +void AudioOutputI2S_F32::scale_f32_to_i16(float32_t *p_f32, float32_t *p_i16, int len) { + for (int i=0; ilength != audio_block_samples) { + Serial.print("AudioOutputI2S_F32: *** WARNING ***: audio_block says len = "); + Serial.print(block_f32->length); + Serial.print(", but I2S settings want it to be = "); + Serial.println(audio_block_samples); + } + //Serial.print("AudioOutputI2S_F32: audio_block_samples = "); + //Serial.println(audio_block_samples); + + //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); + + //count++; + //if (count > 100) { + // Serial.print("AudioOutputI2S_F32::update() orig, scaled = "); + // Serial.print(block_f32->data[30]); + // Serial.print(", "); + // Serial.println(block_f32_scaled->data[30]); + // count=0; + //} + + + //now process the data blocks + __disable_irq(); + if (block_left_1st == NULL) { + block_left_1st = block_f32_scaled; + block_left_offset = 0; + __enable_irq(); + } else if (block_left_2nd == NULL) { + block_left_2nd = block_f32_scaled; + __enable_irq(); + } else { + audio_block_f32_t *tmp = block_left_1st; + block_left_1st = block_left_2nd; + block_left_2nd = block_f32_scaled; + block_left_offset = 0; + __enable_irq(); + AudioStream_F32::release(tmp); + } + AudioStream_F32::transmit(block_f32,0); AudioStream_F32::release(block_f32); //echo the incoming audio out the outputs + } else { + //this branch should never get called, but if it does, let's release the buffer that was never used + AudioStream_F32::release(block_f32_scaled); + } + + block_f32_scaled = block2_f32_scaled; //this is simply renaming the pre-allocated buffer + block_f32 = receiveReadOnly_f32(1); // input 1 = right channel + if (block_f32) { + //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); + + __disable_irq(); + if (block_right_1st == NULL) { + block_right_1st = block_f32_scaled; + block_right_offset = 0; + __enable_irq(); + } else if (block_right_2nd == NULL) { + block_right_2nd = block_f32_scaled; + __enable_irq(); + } else { + audio_block_f32_t *tmp = block_right_1st; + block_right_1st = block_right_2nd; + block_right_2nd = block_f32_scaled; + block_right_offset = 0; + __enable_irq(); + AudioStream_F32::release(tmp); + } + AudioStream_F32::transmit(block_f32,1); AudioStream_F32::release(block_f32); //echo the incoming audio out the outputs + } else { + //this branch should never get called, but if it does, let's release the buffer that was never used + AudioStream_F32::release(block_f32_scaled); + } +} + +#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 == 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_F32::config_i2s(void) { config_i2s(false, AudioOutputI2S_F32::sample_rate_Hz); } +void AudioOutputI2S_F32::config_i2s(bool transferUsing32bit) { config_i2s(transferUsing32bit, AudioOutputI2S_F32::sample_rate_Hz); } +void AudioOutputI2S_F32::config_i2s(float fs_Hz) { config_i2s(false, fs_Hz); } +void AudioOutputI2S_F32::config_i2s(bool transferUsing32bit, float fs_Hz) +{ +#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 + + // change the I2S frequencies to make the requested sample rate + setI2SFreq_T3(fs_Hz); //for T3.x only! + + +#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; //original from Teensy Audio Library + int fs = fs_Hz; + + // 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 +} + +/******************************************************************/ + +// From Chip: The I2SSlave functionality has NOT been extended to allow for different block sizes or sample rates (2020-10-31) + +void AudioOutputI2Sslave_F32::begin(void) +{ + + dma.begin(true); // Allocate the DMA channel first + + //pinMode(2, OUTPUT); + block_left_1st = NULL; + block_right_1st = NULL; + + AudioOutputI2Sslave_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->DADDR = (void *)((uint32_t)&I2S1_TDR1 + 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.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); + 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.enable(); + dma.attachInterrupt(AudioOutputI2S_F32::isr); +} + + + void AudioOutputI2Sslave_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 +} + + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +#if 0 +/* + * output_i2s_OA_F32.cpp (T3.6, T4.0 4Jan20 RSL) + * Revised for floating point input, but totally based + * on and forked from the Teensy Audio Library object output_i2s.cpp. Thus: + * 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. + */ +#include +#include "output_i2s_OA_F32.h" +#include "memcpy_audio.h" +#if defined(__IMXRT1062__) +#include "utility/imxrt_hw.h" +#endif + +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]; + +/* +static inline int32_t f32_to_i32(float32_t f) { + const float fullscale = 1LL << 31; + return max(-(fullscale - 1), min(fullscale - 1, f * fullscale)); +} */ +static void convert_F32toI16(float *in, int16_t *out, int len) { + //WEA Method. Should look at CMSIS arm_float_to_q15 instead: + // https://www.keil.com/pack/doc/CMSIS/DSP/html/group__float__to__x.html#ga215456e35a18db86882e1d3f0d24e1f2 + const float MAX_INT = 32678.0; + for (int i = 0; i < len; i++) { + out[i] = (int16_t)(max(min( (in[i] * MAX_INT), MAX_INT), -MAX_INT)); + } +} + +// high-level explanation of how this I2S & DMA code works: +// https://forum.pjrc.com/threads/65229?p=263104&viewfull=1#post263104 +void AudioOutputI2S_OA_F32::begin(void) { + dma.begin(true); // Allocate the DMA channel first + + AudioOutputI2S_OA_F32::block_left_1st = NULL; + AudioOutputI2S_OA_F32::block_right_1st = NULL; + AudioOutputI2S_OA_F32::config_i2s(); // Get i2s clocks started + +#if defined(KINETISK) + CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0 + + dma.TCD->SADDR = i2s_tx_buffer; // i2s_tx_buffer is buffer for I2S + 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 = AudioStream::update_setup(); + dma.attachInterrupt(isr); +/* // change the I2S frequencies to make the requested sample rate + setI2SFreq(AudioOutputI2S_F32::sample_rate_Hz); + enabled = 1; + */ +} + +// isr() every half update period, about 1450 uSec. Hardware i2s generated. +void AudioOutputI2S_OA_F32::isr(void) { +// T3.x or T4.x +#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 are I16 audio blocks + blockR = AudioOutputI2S_OA_F32::block_right_1st; + offsetL = AudioOutputI2S_OA_F32::block_left_offset; + offsetR = AudioOutputI2S_OA_F32::block_right_offset; + if (blockL && blockR) { // This is the stereo case + 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 { + // fill dest with zeros, AUDIO_BLOCK_SAMPLES *2 bytes (not enough? + // Maybe *4 bytes? This is not usually ever used. + 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); // I16 block + 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 + +#if 0 +//REMOVE 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_F32::update_all(); // _F32 + } 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 +// End of removed < T3.x support + +#endif +} + +void AudioOutputI2S_OA_F32::update(void) { + bool leftExist = false; // Kludge to get to R if L doesn't exist + + // Get a float block to be used for getting L & R data from stream _F32 +// if(millis() > 1200) { Serial.println(" ** update() ** "); } // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + audio_block_f32_t *float_block; + float_block = AudioStream_F32::receiveReadOnly_f32(0); //float data block Left + +// if(millis() > 1200) { Serial.print("float_block address="); Serial.println((uint32_t)float_block); } // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + + if (float_block==NULL) + return; + else + leftExist = true; + + audio_block_t *block; // Int16 block + block = AudioStream::allocate(); // Integer 16 system + if (block==NULL) { + AudioStream_F32::release(float_block); + return; + } + + if (leftExist) { + convert_F32toI16(float_block->data, block->data, float_block->length); +// if(millis() > 1200) { Serial.print(float_block->data[37], 6); Serial.print(" F32 L [37] I16 "); Serial.println(block->data[37]);} //<<<<<<<<<<<<<<<< + } + + // Starting here left channels is regular 16-bit integer + if (leftExist && block != NULL) { // Redundant, block must exist or we wouldn't be here + __disable_irq(); + if (AudioOutputI2S_OA_F32::block_left_1st == NULL) { + AudioOutputI2S_OA_F32::block_left_1st = block; + AudioOutputI2S_OA_F32::block_left_offset = 0; + __enable_irq(); + } else if (block_left_2nd == NULL) { + AudioOutputI2S_OA_F32::block_left_2nd = block; + __enable_irq(); + } else { + audio_block_t *tmp = AudioOutputI2S_OA_F32::block_left_1st; //Check for NULL?? + AudioOutputI2S_OA_F32::block_left_1st = AudioOutputI2S_OA_F32::block_left_2nd; + AudioOutputI2S_OA_F32::block_left_2nd = block; + AudioOutputI2S_OA_F32::block_left_offset = 0; + __enable_irq(); + AudioStream::release(tmp); // I16 block + } + } + AudioStream_F32::release(float_block); + + // Same for the Right channel, if it exists + float_block = AudioStream_F32::receiveReadOnly_f32(1); //float data block Right + if (float_block != NULL) { + convert_F32toI16(float_block->data, block->data, float_block->length); + + +// if(millis() > 1200) { Serial.print(float_block->data[37], 6); Serial.print(" F32 R [37] I16 "); Serial.println(block->data[37]);} //<<<<<<<<<<<< + + + } + + if (block && (float_block != NULL)) { + __disable_irq(); + if (AudioOutputI2S_OA_F32::block_right_1st == NULL) { + AudioOutputI2S_OA_F32::block_right_1st = block; + AudioOutputI2S_OA_F32::block_right_offset = 0; + __enable_irq(); + } else if (AudioOutputI2S_OA_F32::block_right_2nd == NULL) { + AudioOutputI2S_OA_F32::block_right_2nd = block; + __enable_irq(); + } else { + audio_block_t *tmp = AudioOutputI2S_OA_F32::block_right_1st; + AudioOutputI2S_OA_F32::block_right_1st = AudioOutputI2S_OA_F32::block_right_2nd; + AudioOutputI2S_OA_F32::block_right_2nd = block; + AudioOutputI2S_OA_F32::block_right_offset = 0; + __enable_irq(); + AudioStream::release(tmp); + } + } + +/* + if(millis() > 1200) { Serial.print("Endupdate, fb="); Serial.println((uint32_t)float_block); + Serial.print(" b="); Serial.println((uint32_t)block); } // <<<<<<<<<<<<<<<< +*/ + + + AudioStream_F32::release(float_block); + AudioStream::release(block); +} // 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; // 32-bit Transmit Word Mask, 0=enabled + + 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 = AudioStream_F32::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 +} + + +#endif diff --git a/output_i2s_f32.h b/output_i2s_f32.h new file mode 100644 index 0000000..f0ec988 --- /dev/null +++ b/output_i2s_f32.h @@ -0,0 +1,234 @@ +/* + * ***** output_i2s_f32.h ***** + * + + * 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. + */ + + /* + * 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 +#include +#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: + AudioOutputI2S_F32(void) : AudioStream_F32(2, inputQueueArray) { begin();} //uses default AUDIO_SAMPLE_RATE and BLOCK_SIZE_SAMPLES from AudioStream.h + 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); + void begin(bool); + void sub_begin_i32(void); + void sub_begin_i16(void); + friend class AudioInputI2S_F32; + + friend class AudioInputI2S_F32; + #if defined(__IMXRT1062__) + friend class AudioOutputI2SQuad_F32; + friend class AudioInputI2SQuad_F32; + //friend class AudioOutputI2SHex; + //friend class AudioInputI2SHex; + //friend class AudioOutputI2SOct; + //friend class AudioInputI2SOct; + #endif + + 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_T3(const float); + + + +protected: + AudioOutputI2S_F32(int dummy): AudioStream_F32(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !! + static void config_i2s(void); + static void config_i2s(bool); + static void config_i2s(float); + static void config_i2s(bool, float); + //static void config_i2s_i16(void,float); + //static void config_i2s_i32(void,float); + 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_16(void); + static void isr_32(void); + static void isr(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; +}; + + +class AudioOutputI2Sslave_F32 : public AudioOutputI2S_F32 +{ +public: + AudioOutputI2Sslave_F32(void) : AudioOutputI2S_F32(0) { begin(); } ; + void begin(void); + friend class AudioInputI2Sslave_F32; + friend void dma_ch0_isr(void); +protected: + static void config_i2s(void); +}; + + + +#endif + + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +#if 0 +/* + * output_i2s_OA_F32.h Revised for floating point input, but totally based + * on and forked from the Teensy Audio Library object output_i2s.cpp. Thus: + * + * 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. + */ +// 28 Dec 2020 Tested, master clock, at 44100, on T3.6 and T4.0 without problems Bob L. + +#ifndef output_i2s_oa_f32_h_ +#define output_i2s_oa_f32_h_ + +#include "Arduino.h" +#include "Audio.h" +#include "OpenAudio_ArduinoLibrary.h" +#include "AudioStream.h" +#include "AudioStream_F32.h" +#include "DMAChannel.h" +// #include "pins_teensy.c" + +// Info: AUDIO_BLOCK_SAMPLES is 128, from AudioStream.h +// AUDIO_SAMPLE_RATE is 44117.64706 from AudioStream.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_f32) { + //sample_rate_Hz = AUDIO_SAMPLE_RATE; + //audio_block_samples = AUDIO_BLOCK_SAMPLES; + begin(); + } + // AudioOutputI2S_OA_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) {begin(); }; + + virtual void update(void); + void begin(void); + //friend class AudioInputI2S_OA_F32; + +#if defined(__IMXRT1062__) +/* + friend class AudioOutputI2S_OA_F32Quad; + friend class AudioInputI2SQuad; + friend class AudioOutputI2S_OA_F32Hex; + friend class AudioInputI2SHex; + friend class AudioOutputI2S_OA_F32Oct; + friend class AudioInputI2SOct; + */ +#endif + static void config_i2s(void); + + // volatile uint8_t enabled = 1; + +protected: + AudioOutputI2S_OA_F32(int dummy): AudioStream_F32(2, inputQueueArray_f32) {} // to be used only inside AudioOutputI2S_OA_F32slave !! + + 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 uint32_t usec, usecLast; + static float sample_rate_Hz; + static int audio_block_samples; + 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_f32[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; + friend void dma_ch0_isr(void); +protected: + static void config_i2s(void); +}; + +#endif + +#endif diff --git a/play_queue_f32.cpp b/play_queue_f32.cpp index 8583faf..76ad25e 100644 --- a/play_queue_f32.cpp +++ b/play_queue_f32.cpp @@ -1,10 +1,10 @@ -/* -* AudioRecordQueue_F32 +/* +* AudioRecordQueue_F32 * -* Created: Chip Audette (OpenAudio), Feb 2017 +* Created: Chip Audette (OpenAudio), Feb 2017 * Extended from on Teensy Audio Library * -* License: MIT License. Use at your own risk. +* License: MIT License. Use at your own risk. */ #include "play_queue_f32.h" @@ -12,63 +12,75 @@ bool AudioPlayQueue_F32::available(void) { - if (userblock) return true; - userblock = allocate_f32(); - if (userblock) return true; - return false; + if (userblock) return true; + userblock = allocate_f32(); + if (userblock) return true; + return false; } +/* getBuffer() returns a pointer to the data area of an AudioBlock_32 + * that can be loaded in the .INO. There is only one of these at a + * time, and they hold 128 float32_t. allocate_f32 will hold up + * a return from getBuffer() if Audio memory is not available. This will + * be freed up by update(). + */ float32_t * AudioPlayQueue_F32::getBuffer(void) { - if (userblock) return userblock->data; - while (1) { - userblock = allocate_f32(); - if (userblock) return userblock->data; - yield(); - } + if (userblock) return userblock->data; + while (1) { + userblock = allocate_f32(); + if (userblock) return userblock->data; + yield(); + } } +/* playBuffer() can be called anytime after data is + * loaded to the data block pointed to by getBuffer). + * This function then enters the pointer to the queue, + * waiting to be sent in turn. If the queue is full, + * this function waits until a spot in the queue is opened + * up by update() (called by interrupts). + */ void AudioPlayQueue_F32::playBuffer(void) { - uint32_t h; + uint32_t h; - if (!userblock) return; - h = head + 1; - if (h >= 32) h = 0; - while (tail == h) ; // wait until space in the queue - queue[h] = userblock; - head = h; - userblock = NULL; + if (!userblock) return; + h = head + 1; + if (h >= 32) h = 0; + while (tail == h) ; // wait until space in the queue + queue[h] = userblock; + head = h; + userblock = NULL; } void AudioPlayQueue_F32::update(void) { - audio_block_f32_t *block; - uint32_t t; + audio_block_f32_t *block; + uint32_t t; - t = tail; - if (t != head) { - if (++t >= 32) t = 0; - block = queue[t]; - tail = t; - transmit(block); - release(block); - } + t = tail; + if (t != head) { // a data block is available to transmit out + if (++t >= 32) t = 0; // tail is advanced by one, circularly + block = queue[t]; // pointer to next block + tail = t; + transmit(block); + release(block); + } } //assume user already has an audio_block that was NOT allocated by this //playBuffer. Here, you hand it your buffer. This object takes ownership //of it and puts it into the queue void AudioPlayQueue_F32::playAudioBlock(audio_block_f32_t *audio_block) { - uint32_t h; + uint32_t h; - if (!audio_block) return; - h = head + 1; - if (h >= 32) h = 0; - while (tail == h) ; // wait until space in the queue - queue[h] = audio_block; - audio_block->ref_count++; //take ownership of this block - head = h; - //userblock = NULL; - + if (!audio_block) return; + h = head + 1; + if (h >= 32) h = 0; + while (tail == h) ; // wait until space in the queue + queue[h] = audio_block; + audio_block->ref_count++; //take ownership of this block + head = h; + //userblock = NULL; } diff --git a/play_queue_f32.h b/play_queue_f32.h index 80f8d4c..420e15b 100644 --- a/play_queue_f32.h +++ b/play_queue_f32.h @@ -1,11 +1,30 @@ /* -* AudioPlayQueue_F32 +* AudioPlayQueue_F32 * -* Created: Chip Audette (OpenAudio), Feb 2017 +* Created: Chip Audette (OpenAudio), Feb 2017 * Extended from on Teensy Audio Library * -* License: MIT License. Use at your own risk. +* License: MIT License. Use at your own risk. +* */ +/* Notes from Paul Stoffregen (from 4320 LED Video+Sound Project) + * + * AudioPlayQueue - Play audio data provided by the Arduino sketch. + * This object provides functions to allow the sketch code to push data + * into the audio system. + * + * getBuffer(); + * Returns a pointer to an array of 128 int16. + * This buffer is within the audio library memory pool, providing the most + * efficient way to input data to the audio system. The buffer is likely + * to be populated by previously used data, so the entire 128 words should + * be written before calling playBuffer(). Only a single buffer should be + * requested at a time. This function may return NULL if no memory + * is available. + * + * playBuffer(); + * Transmit the buffer previously obtained from getBuffer(). + */ #ifndef play_queue_f32_h_ #define play_queue_f32_h_ @@ -17,25 +36,25 @@ class AudioPlayQueue_F32 : public AudioStream_F32 { //GUI: inputs:0, outputs:1 //this line used for automatic generation of GUI node public: - AudioPlayQueue_F32(void) : AudioStream_F32(0, NULL), - userblock(NULL), head(0), tail(0) { } - AudioPlayQueue_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL), - userblock(NULL), head(0), tail(0) { } - //void play(int16_t data); - //void play(const int16_t *data, uint32_t len); - //void play(float32_t data); - //void play(const float32_t *data, uint32_t len); - void playAudioBlock(audio_block_f32_t *); - bool available(void); - float32_t * getBuffer(void); - void playBuffer(void); - void stop(void); - //bool isPlaying(void) { return playing; } - virtual void update(void); + AudioPlayQueue_F32(void) : AudioStream_F32(0, NULL), + userblock(NULL), head(0), tail(0) { } + AudioPlayQueue_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL), + userblock(NULL), head(0), tail(0) { } + //void play(int16_t data); + //void play(const int16_t *data, uint32_t len); + //void play(float32_t data); + //void play(const float32_t *data, uint32_t len); + void playAudioBlock(audio_block_f32_t *); // Not in I16 library + bool available(void); + float32_t * getBuffer(void); + void playBuffer(void); + void stop(void); + //bool isPlaying(void) { return playing; } + virtual void update(void); private: - audio_block_f32_t *queue[32]; - audio_block_f32_t *userblock; - volatile uint8_t head, tail; + audio_block_f32_t *queue[32]; + audio_block_f32_t *userblock; + volatile uint8_t head, tail; }; #endif diff --git a/synth_GaussianWhiteNoise_F32.cpp b/synth_GaussianWhiteNoise_F32.cpp index b1dc332..cd4a1b9 100644 --- a/synth_GaussianWhiteNoise_F32.cpp +++ b/synth_GaussianWhiteNoise_F32.cpp @@ -1,9 +1,9 @@ /* * synth_GaussianWhiteNoise_F32.cpp * by Bob Larkin W7PUA 15 June 2020 - * - * Created: Chip Audette, OpenAudio, Feb 2017 - + * + * Created: Chip Audette, OpenAudio, Feb 2017 + * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -32,25 +32,25 @@ void AudioSynthGaussian_F32::update(void) { - audio_block_f32_t *blockOut; - uint32_t it; - float32_t rdev = 0.0f; + audio_block_f32_t *blockOut; + uint32_t it; + float32_t rdev = 0.0f; float32_t* pd; - if (sd < 0.01f) { - return; // Not enabled - } + if (sd < 0.01f) { + return; // Not enabled + } #if TEST_TIME_GWN if (iitt++ >1000000) iitt = -10; uint32_t t1, t2; t1 = tElapse; #endif - blockOut = AudioStream_F32::allocate_f32(); - if (!blockOut) { - if(errorPrint) Serial.println("GWN-ERR: No output memory"); - return; - } + blockOut = AudioStream_F32::allocate_f32(); + if (!blockOut) { + if(errorPrint) Serial.println("GWN-ERR: No output memory"); + return; + } pd = &blockOut->data[0]; // Pointer to write data /* The "Even Quicker" uniform random sample generator from D. E. Knuth and @@ -58,44 +58,34 @@ 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 { + union { uint32_t i32; float32_t f32; } uinf; - - - for(int i=0; ilength; - ph = phase_accumulator; - inc = phase_increment; - for (i=0; i < block_length; i++) { - index = ph >> 24; - val1 = AudioWaveformSine[index]; - val2 = AudioWaveformSine[index+1]; - scale = (ph >> 8) & 0xFFFF; - val2 *= scale; - val1 *= 0x10000 - scale; - #if (defined(KINETISK) || defined(__IMXRT1062__) ) - block->data[i] = (float) multiply_32x32_rshift32(val1 + val2, magnitude); - #elif defined(KINETISL) - block->data[i] = (float) ((((val1 + val2) >> 16) * magnitude) >> 16); - #endif - ph += inc; - block->data[i] = block->data[i] / 32767.0f; // scale to float - } - phase_accumulator = ph; - - AudioStream_F32::transmit(block); - AudioStream_F32::release(block); - return; - } - } - phase_accumulator += phase_increment * block_length; - } +void AudioSynthWaveformSine_F32::update(void) { + audio_block_f32_t *block; + uint32_t i, ph, inc, index, scale; + int32_t val1, val2; + static uint32_t block_length = 0; + + if (enabled) { + if (magnitude) { + block = allocate_f32(); + if (block) { + block_length = (uint32_t)block->length; + ph = phase_accumulator; + inc = phase_increment; + for (i=0; i < block_length; i++) { + index = ph >> 24; + val1 = AudioWaveformSine[index]; + val2 = AudioWaveformSine[index+1]; + scale = (ph >> 8) & 0xFFFF; + val2 *= scale; + val1 *= 0x10000 - scale; +#if (defined(KINETISK) || defined(__IMXRT1062__) ) + block->data[i] = (float) multiply_32x32_rshift32(val1 + val2, magnitude); +#elif defined(KINETISL) + block->data[i] = (float) ((((val1 + val2) >> 16) * magnitude) >> 16); +#endif + ph += inc; + block->data[i] = block->data[i] / 32767.0f; // scale to float + } + phase_accumulator = ph; + + AudioStream_F32::transmit(block); + AudioStream_F32::release(block); + return; + } + }// end if (magnitude) + phase_accumulator += phase_increment * block_length; // continue sine wave while magnitude==0 + } // end if (enabled) } - - - - - -