Hi all!
I was having issues with the original teensy handling of audio. The input and output were not keeping sync. So I got to this thread and alex6679's code.
That implementation works!
I do believe the original teensy audio has some issue with the way the supposed async is implemented for input + output.
Having fixed the issue with the poor sync I continued with other things...
I was trying to stream teensy's ADC along with I2S, but found that I would have issues with the ADC channel dropping samples (showing 128 zeros, followed by an overshot of values). See the image. Audio shield line out 1 was tied to ADC input pin via a resistor and to line in 0 via another resistor (that is the reason for the difference in signal scales, please don't mind it). The signal frequency was set at 44.1kHz/128.
I noticed that the issue would be exacerbated at lower CPU speeds (I actually don't see the issue when running overclocked @816MHz) and seeing that alex6679's code uses up more CPU time I had a look for bottlenecks.
Here are some benchmarks. All use teensy 4.0, audio shield, 600Mhz CPU speed, 2ch, 44.1kHz:
Running the original teensy audio and only using I2S
Running the same using alex6679's code I get 3.20%.
Running the original teensy audio and streaming the ADC on 2 ch instead of i2sIN
Running the same using alex6679's code I get 4.85%. And I get the issue (image above).
Full test code. Use #define USEADC to test with ADC instead of I2S:
The AudioProcessorUsageMax() still has a lot of headroom, but still... I get issues with ADC.
I am not fluent in c/c++, nor this code base...
I did find out that the smoothing of the update call times is taking up a lot of cycles! I assume the following lines are doing some sort of rolling average?
/usb_audio_interface.cpp in USBAudioInInterface::update:
For reference, replacing History with current values:
Removing "History" makes the problem almost go away, but not fully.
I assume the issue is with disabling interrupts or handling them in time? So I think the code inside the disable_irq is more critical!?
As I didn't write the code base and am not familiar with it I wanted to ask you @alex6679 if it would be possible to replace the smoothing with a simpler approach:
I was having issues with the original teensy handling of audio. The input and output were not keeping sync. So I got to this thread and alex6679's code.
That implementation works!
I do believe the original teensy audio has some issue with the way the supposed async is implemented for input + output.
Having fixed the issue with the poor sync I continued with other things...
I was trying to stream teensy's ADC along with I2S, but found that I would have issues with the ADC channel dropping samples (showing 128 zeros, followed by an overshot of values). See the image. Audio shield line out 1 was tied to ADC input pin via a resistor and to line in 0 via another resistor (that is the reason for the difference in signal scales, please don't mind it). The signal frequency was set at 44.1kHz/128.
I noticed that the issue would be exacerbated at lower CPU speeds (I actually don't see the issue when running overclocked @816MHz) and seeing that alex6679's code uses up more CPU time I had a look for bottlenecks.
Here are some benchmarks. All use teensy 4.0, audio shield, 600Mhz CPU speed, 2ch, 44.1kHz:
Running the original teensy audio and only using I2S
Using:AudioConnection patchCord1(i2sIN, 0, usbOUT, 0);
AudioConnection patchCord2(i2sIN, 1, usbOUT, 1);
AudioConnection patchCord3(usbIN, 0, i2sOUT, 0);
AudioConnection patchCord4(usbIN, 1, i2sOUT, 1);
I get 0.04%.AudioProcessorUsageMax()
Running the same using alex6679's code I get 3.20%.
Running the original teensy audio and streaming the ADC on 2 ch instead of i2sIN
I get 1.96%. And apparently no drops in ADC.AudioConnection patchCord2(adcIN, 0, usbOUT, 1);
Running the same using alex6679's code I get 4.85%. And I get the issue (image above).
Full test code. Use #define USEADC to test with ADC instead of I2S:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#define USEADC //toggle ADC vs I2S input
AudioInputI2S i2sIN;
AudioInputUSB usbIN;
AudioOutputI2S i2sOUT;
AudioOutputUSB usbOUT;
AudioConnection patchCord1(i2sIN, 0, usbOUT, 0);
#ifdef USEADC
AudioInputAnalog adcIN;
AudioConnection patchCord2(adcIN, 0, usbOUT, 1);
#else
AudioConnection patchCord2(i2sIN, 1, usbOUT, 1);
#endif
AudioConnection patchCord3(usbIN, 0, i2sOUT, 0);
AudioConnection patchCord4(usbIN, 1, i2sOUT, 1);
AudioControlSGTL5000 sgtl5000_1;
void setup() {
AudioMemory(20); //enough for all tests
sgtl5000_1.enable();
sgtl5000_1.volume(0.15);
}
unsigned long counter = 0;
void loop() {
if(millis() - counter > 1000){
Serial.println();
Serial.print("CPU:");
Serial.print(AudioProcessorUsage());
Serial.print(",");
Serial.print(AudioProcessorUsageMax());
Serial.print(" ");
Serial.print("Memory: ");
Serial.print(AudioMemoryUsage());
Serial.print(",");
Serial.println(AudioMemoryUsageMax());
counter = millis();
}
}
The AudioProcessorUsageMax() still has a lot of headroom, but still... I get issues with ADC.
I am not fluent in c/c++, nor this code base...
I did find out that the smoothing of the update call times is taking up a lot of cycles! I assume the following lines are doing some sort of rolling average?
/usb_audio_interface.cpp in USBAudioInInterface::update:
and USBAudioOutInterface::update:History<50> historyUpdate = _lastCallUpdate.getHistory();
_updateCurrentSmoothPending = _lastCallUpdate.getLastCall<20>(historyUpdate, blockDuration*F_CPU_ACTUAL);
Reducing the history from 20 to 5 improves performance (I2S test case; from 3.20% to 1.50%)!History<50> historyUpdate = _lastCallUpdate.getHistory();
double updateCurrentSmooth = _lastCallUpdate.getLastCall<20>(historyUpdate, blockDuration*F_CPU_ACTUAL);
For reference, replacing History with current values:
and_updateCurrentSmoothPending= t;
improves from 3.20% to 0.37%! So this History is a major burden.double updateCurrentSmooth = clockCount;
Removing "History" makes the problem almost go away, but not fully.
I assume the issue is with disabling interrupts or handling them in time? So I think the code inside the disable_irq is more critical!?
As I didn't write the code base and am not familiar with it I wanted to ask you @alex6679 if it would be possible to replace the smoothing with a simpler approach:
weight = 0.2 //chose higher or lower for more or less smoothing
smooth_val = smooth_val * weight + new_val * (1 - weight)

