Audio Queue blocks RMS and PEAK

Status
Not open for further replies.

DIYLAB

Well-known member
Uses:
Teensy 4.0 (600MHz clock)
Audio Adaptor Rev.D
Display ILI9341 240x320
Display Driver: ILI9341_t3n
Teensyduino, Version 1.54

---------------------------------------------------

Houston, we have a problem ;o)

This small and executable program is a spin-off from a project, edited to the most necessary for the forum.

Code:
#include <Wire.h>
#include "SPI.h"
#include <Audio.h>
#include <ILI9341_t3n.h>
#include <ili9341_t3n_font_Arial.h>
#include <ili9341_t3n_font_ArialBold.h>

#define TFT_DC  9
#define TFT_CS 10
#define TFT_RESET 255 // vcc

ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RESET);

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=205,270
AudioOutputI2S           i2s2;           //xy=233.00000381469727,400.0000057220459
AudioAnalyzeFFT1024      fft1024_1;      //xy=425.00000762939453,132.00000190734863
AudioAnalyzeFFT1024      fft1024_2;      //xy=426.00000381469727,320.0000047683716
AudioAnalyzeRMS          rms1;           //xy=431.00000381469727,93.00000190734863
AudioRecordQueue         queue2;         //xy=431.00000381469727,280.00000381469727
AudioAnalyzePeak         peak1;          //xy=432.00000762939453,54.000003814697266
AudioRecordQueue         queue1;         //xy=432.00000381469727,171.00000381469727
AudioMixer4              mixer1;         //xy=432.00000381469727,225.00000381469727
AudioAnalyzeRMS          rms2;           //xy=433.00000762939453,359.0000047683716
AudioAnalyzePeak         peak2;          //xy=435.00000762939453,399.0000057220459
AudioAnalyzeFFT1024      fft1024_3;      //xy=566.0000076293945,225.00000286102295
AudioConnection          patchCord1(i2s1, 0, queue1, 0);
AudioConnection          patchCord2(i2s1, 0, peak1, 0);
AudioConnection          patchCord3(i2s1, 0, rms1, 0);
AudioConnection          patchCord4(i2s1, 0, fft1024_1, 0);
AudioConnection          patchCord5(i2s1, 0, mixer1, 0);
AudioConnection          patchCord6(i2s1, 0, i2s2, 0);
AudioConnection          patchCord7(i2s1, 1, queue2, 0);
AudioConnection          patchCord8(i2s1, 1, fft1024_2, 0);
AudioConnection          patchCord9(i2s1, 1, rms2, 0);
AudioConnection          patchCord10(i2s1, 1, peak2, 0);
AudioConnection          patchCord11(i2s1, 1, mixer1, 1);
AudioConnection          patchCord12(i2s1, 1, i2s2, 1);
AudioConnection          patchCord13(mixer1, fft1024_3);
AudioControlSGTL5000     sgtl5000_1;     //xy=216,96.00000667572021
// GUItool: end automatically generated code

// Sample Buffers
int16_t samplesLeft[2048] = { 0 };
int16_t samplesRight[2048] = { 0 };

// FrameCounter
uint32_t fcl = 0, fcr = 0;

void setup() {
    // Audio
    AudioMemory(32);
    sgtl5000_1.enable();
    sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
    sgtl5000_1.lineOutLevel(13); // 3.16 Volts p-p
    sgtl5000_1.volume(.7); // 0.8 corresponds to the maximum undistorted output for a full scale signal.
    sgtl5000_1.lineInLevel(0); // 3.12 Volts p-p

    // Start sample queue.
    queue1.begin();
    queue2.begin();

    // Display
    tft.begin();

    tft.fillScreen(ILI9341_BLACK);
    tft.setFont(Arial_10_Bold);
    tft.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
    tft.drawString("LEFT CHANNEL", 8, 30);
    tft.drawString("RIGHT CHANNEL", 8, 100);
    tft.setFont(Arial_10);
    tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
    tft.drawString("rms1:", 8, 50);
    tft.drawString("peak1:", 8, 70);
    tft.drawString("rms2:", 8, 120);
    tft.drawString("peak2:", 8, 140);
}

void loop() {
    getSamples(1);
    drawNeedleLeft();
    drawNeedleRight();
}

/// <summary>
/// Get samples for left and right channel.
/// </summary>
/// <param name="blocks"></param>
void getSamples(byte blocks) {
    if (queue1.available() >= blocks && queue2.available() >= blocks) {
        for (byte i = 0; i < blocks; i++) {
            memcpy(&samplesLeft[i * 128], queue1.readBuffer(), 256);
            memcpy(&samplesRight[i * 128], queue2.readBuffer(), 256);
            queue1.freeBuffer();
            queue2.freeBuffer();
        }
    }
}

/// <summary>
/// Draw left Side
/// </summary>
void drawNeedleLeft() {
    if (++fcl > 500000 && rms1.available() && peak1.available()) {
        tft.drawFloat(rms1.read(), 3, 70, 50);
        tft.drawFloat(peak1.read(), 3, 70, 70);
        fcl = 0;
    }
}

/// <summary>
/// Draw right Side
/// </summary>
void drawNeedleRight() {
    if (++fcr > 500000 && rms2.available() && peak2.available()) {
        tft.drawFloat(rms2.read(), 3, 70, 120);
        tft.drawFloat(peak2.read(), 3, 70, 140);
        fcr = 0;
    }
}

In the whole project there are some modules which use AudioLib and need FFT, RMS, PEAK.
In this extraction only RMS and PEAK are interesting.
The original looks like this (an analog VU meter is drawn):

analog_black.png analog_warm.png analog_white.png

The SGTL5000 scheme of the whole project:

sgtl5000_scheme.png

In the test program the display shows RMS1, RMS2, PEAK1 and PEAK2 and only every 500000 frames to calm the display for you.

Now to the problem:
If I don't fetch the samples in the main loop and comment out getSamples(1);, no more RMS and PEAK arrive!

I would love to stop the queue completely for this module and only turn it back on for modules that really need it, but that doesn't work properly either, the stopped queue doesn't always restart.

So the goal is to run the queue and still get RMS and PEAK reliably when getSamples(1); is not present in the main loop .
What am I doing wrong?
 
Last edited:
You don't free the queue buffers anymore, then - they stay allocated and after some time there are no free blocks anymore.
 
You don't free the queue buffers anymore, then - they stay allocated and after some time there are no free blocks anymore.

Then I guess it should go like this?

Code:
void loop() {
    getSamples(0);
    drawNeedleLeft();
    drawNeedleRight();
}

Code:
/// <summary>
/// Get samples for left and right channel.
/// </summary>
/// <param name="blocks"></param>
void getSamples(byte blocks) {
    if (blocks == 0) {
        queue1.freeBuffer();
        queue2.freeBuffer();
    } else {
        if (queue1.available() >= blocks && queue2.available() >= blocks) {
            for (byte i = 0; i < blocks; i++) {
                memcpy(&samplesLeft[i * 128], queue1.readBuffer(), 256);
                memcpy(&samplesRight[i * 128], queue2.readBuffer(), 256);
                queue1.freeBuffer();
                queue2.freeBuffer();
            }
        }
    }
}

Unfortunately, that does not work either.
Or is with free something else meant than freeBuffer() ?
 
You must call readBuffer before freeBuffer to really free it

That's exactly the problem - it takes time.
In this analog VU module, I need passes to be as fast as they can be, since I'm now drawing the pointers with edge smoothing.

If I read at least one buffer, it costs time.
But apparently it works with clear().

Code:
/// <summary>
/// Get samples for left and right channel.
/// </summary>
/// <param name="blocks"></param>
void getSamples(byte blocks) {
    if (blocks == 0 && (queue1.available() > 16 || queue2.available() > 16)) {
        queue1.clear();
        queue2.clear();
    } else {
        if (queue1.available() >= blocks && queue2.available() >= blocks) {
            for (byte i = 0; i < blocks; i++) {
                memcpy(&samplesLeft[i * 128], queue1.readBuffer(), 256);
                memcpy(&samplesRight[i * 128], queue2.readBuffer(), 256);
                queue1.freeBuffer();
                queue2.freeBuffer();
            }
        }
    }
}

It would be best if I don't have to worry about the queue at all in the analog module and it doesn't overflow ;o)
Is there maybe a limit somewhere that can be set?
I never need more than 16 blocks per channel in the different modules.
Or is there an ISR vector where I could clean up?
 
That's exactly the problem - it takes time.
Code:
    if (blocks == 0) {
        queue1.readBuffer();
        queue1.freeBuffer();
        queue2.readBuffer();
        queue2.freeBuffer();
    }
does not take so much time. readBuffer() only passes an address. You have not to copy the buffer.
 
Oh, your solution is very interesting, thank you!
Some things are so tangible and yet so distant :)

I don't want to open a new thread, because I think my next question fits well with the topic:

How can I measure the cycle time as accurately as possible and output it in a suitable way?
I would like to know the difference between

Code:
 queue1.readBuffer();
 queue1.freeBuffer();
 queue2.readBuffer();
 queue2.freeBuffer();

and

Code:
queue1.clear();
queue2.clear();

- perhaps in microseconds?
 
Your solution is the clear winner!

The output oscillates between 0 and 1 microseconds.
With clear() the output is 3 microseconds.

I hope this thread will help other users who want or need to draw quickly.

Code:
/// <summary>
/// Get samples for left and right channel.
/// </summary>
/// <param name="blocks"></param>
void getSamples(byte blocks) {
    if (blocks == 0) {
       uint32_t tMicros = micros();
       
        //queue1.clear();
        //queue2.clear();

        queue1.readBuffer();
        queue1.freeBuffer();
        queue2.readBuffer();
        queue2.freeBuffer();

       Serial.println(micros() - tMicros);
    } else {
        if (queue1.available() >= blocks && queue2.available() >= blocks) {
            for (byte i = 0; i < blocks; i++) {
                memcpy(&samplesLeft[i * 128], queue1.readBuffer(), 256);
                memcpy(&samplesRight[i * 128], queue2.readBuffer(), 256);
                queue1.freeBuffer();
                queue2.freeBuffer();
            }
        }
    }
}
 
Code:
void AudioRecordQueue::clear(void)
{
	uint32_t t;

	if (userblock) {
		release(userblock);
		userblock = NULL;
	}
	t = tail;
	while (t != head) {
		if (++t >= max_buffers) t = 0;
		release(queue[t]);
	}
	tail = t;
}

int16_t * AudioRecordQueue::readBuffer(void)
{
	uint32_t t;

	if (userblock) return NULL;
	t = tail;
	if (t == head) return NULL;
	if (++t >= max_buffers) t = 0;
	userblock = queue[t];
	tail = t;
	return userblock->data;
}

void AudioRecordQueue::freeBuffer(void)
{
	if (userblock == NULL) return;
	release(userblock);
	userblock = NULL;
}
clear() frees all data blocks, freeBuffer() only a single buffer (the last one).
 
Very informative, thank you very much!
The program now runs for several hours without problems, the needles fidget in the usual way as before, I am happy.
 
Status
Not open for further replies.
Back
Top