Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 10 of 10

Thread: Audio Queue blocks RMS and PEAK

  1. #1
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64

    Audio Queue blocks RMS and PEAK

    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):

    Click image for larger version. 

Name:	analog_black.png 
Views:	28 
Size:	5.5 KB 
ID:	25354 Click image for larger version. 

Name:	analog_warm.png 
Views:	33 
Size:	5.8 KB 
ID:	25355 Click image for larger version. 

Name:	analog_white.png 
Views:	28 
Size:	6.2 KB 
ID:	25356

    The SGTL5000 scheme of the whole project:

    Click image for larger version. 

Name:	sgtl5000_scheme.png 
Views:	32 
Size:	21.4 KB 
ID:	25357

    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 by DIYLAB; 07-25-2021 at 12:04 PM.

  2. #2
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    9,317
    You don't free the queue buffers anymore, then - they stay allocated and after some time there are no free blocks anymore.

  3. #3
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64
    Quote Originally Posted by Frank B View Post
    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() ?

  4. #4
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,318
    You must call readBuffer before freeBuffer to really free it

  5. #5
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64
    Quote Originally Posted by WMXZ View Post
    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?

  6. #6
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,318
    Quote Originally Posted by DIYLAB View Post
    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.

  7. #7
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64
    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?

  8. #8
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64
    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();
                }
            }
        }
    }

  9. #9
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,318
    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).

  10. #10
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64
    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •