Best method to display a moving waveform?

Got the code ported to V8.4.0
I have a meter set up to show relative track position, and I have modified my buffer and the waveform create code:

#define BLUE_COLOR 0x0000FF // Example color value for blue
#define WAVEFORM_WIDTH 800
#define WAVEFORM_HEIGHT 128
EXTMEM static uint8_t waveformbuffer[WAVEFORM_WIDTH * WAVEFORM_HEIGHT / 2] __attribute__((aligned(32)));

void createDynamicWaveform(){
  uint8_t * buf = (uint8_t *)waveformbuffer;
  memset(buf, B00010001, WAVEFORM_WIDTH /2 * WAVEFORM_HEIGHT);

  waveform_canvas = lv_canvas_create(screen);
  lv_canvas_set_buffer(waveform_canvas, buf, WAVEFORM_WIDTH, WAVEFORM_HEIGHT, LV_IMG_CF_INDEXED_4BIT);
  lv_obj_center(waveform_canvas);
  lv_canvas_set_palette(waveform_canvas, 1, LV_COLOR_MAKE(0xFF, 0xFF, 0xFF));
  lv_canvas_set_palette(waveform_canvas, 0, LV_COLOR_MAKE(0x00, 0x00, 0x00));

  //memset(buf, B00010001, WAVEFORM_WIDTH * WAVEFORM_HEIGHT / 2);
  lv_obj_invalidate(waveform_canvas);
}

But, I notice the canvas is missing some pixels on the bottom right of the canvas area - what am I missing here?

1 Like

Look up :slight_smile: The last part of my previous post, I mention the canvas stores the palette followed by the data in the buffer, so you need to give the canvas a buffer with the extra space for the palette, and do you drawing operations on the buffer offset by that space…

When I cut and paste and edited sort-of pseudo-code in my earlier posts, I forgot to show that distinction!

1 Like

This ^
So it would not let me compile in PlatformIO with + paletteOffset so I just did this:

uint16_t paletteOffset = 16 * sizeof(lv_color32_t); //For indexed 4 bit
DMAMEM uint8_t waveformbuffer[(WAVEFORM_WIDTH * WAVEFORM_HEIGHT / 2 ) + (16*4)]__attribute__((aligned(32)));
uint8_t * buf = (uint8_t *)waveformbuffer;
uint8_t * bufData = buf + paletteOffset;

Ugh, my bad, my code base obviously has way more so I’m cutting and pasting stuff and editing in here :slight_smile: It needs to be const, will update…

1 Like

I myslef am under the weather today, and so I am not paying attention to half of what comes across my sight at the moment :sweat_smile:

Hahaha, feel better, and it’s OK, I’m also distracted adding this great new feature idea I came up with all by myself of showing RMS and peak amplitude on my audio spectrum graph!

1 Like

Still under the weather here, so very slow on dev.
Next task is to implement the draw V line functionality and see if I can get it to draw the waveform

Ok, good luck! Two things that always get me when I jump back into my project each time - the drawVLine function, {0,0} is top left of the canvas, and you’ll also need to erase the contents of the data buffer before each draw refresh! I’m a {0,0} bottom left kinda guy, so it always confuses me :slight_smile:

@egonbeermat I’m going to just use drawPixel as I already have a routine to redraw the background color where needed for each line (to save time instead of using memset before each frame)

But I am struggling to understand how to set the color value (is this basically an index to the set palettes?) as well as the stride value (is it always w/2?)

FASTRUN void drawPixel(int16_t x, int16_t y, uint8_t color, uint8_t * buffer, uint16_t stride)
{
  uint8_t *pBuf = buffer + (x / 2) + (y * stride);

  if (x % 2 == 0) {
    //Even - Place in high bytes
    *pBuf = (color << 4) | (*pBuf & 0x0F);
  } else {
    //Odd - Place in low bytes
    *pBuf = color | (*pBuf & 0xF0);
  }
} 

Yes, color is the index of the color you created in the palette, and stride is w/2 in this case, for 4 bit. Stride is a common term for how many bytes to jump in a buffer to the next pixel line…w / 2 for 4 bit, w * 2 for 16 bit, and some formats may have extra internal data at the end of each line, etc.

Iterating drawPixel will be slower than calling memset, though, especially for clearing out the whole canvas each time, but even if you ‘intelligently’ erase the remaining vertical area after drawing a line. All the loop overhead, and addressing each byte twice etc. I just ran a quick test, comparative times of memset vs loop over x, y with drawPixel:

memset: Width: 474, height: 110: Time: 43 micros
drawPixel: Width: 474, height: 110: Time: 843 micros

I guess getting it working first, then speed up :slight_smile: These aren’t performance destroying numbers for proof of concept…but I tried all these ways to erase what came before, drawing lines in the background color for the remaining part of the y line for line I drew, etc. Because memset is 20x times faster, you only get a performance boost of erasing the background with drawPixel vs memset if your graph lines cover 95% of the canvas, and you only need to erase 5%…

1 Like

Great! implemented and compiled it now!
See, I learn new stuff every day, thanks to the experts like yourself :palms_up_together:

Last Q regarding memset - to set the entire canvas to a color, I need to set each 4 bits to the value of the desired index, correct?
So for an index of 2 for example, I would set B00100010 ?

1 Like

Yes, exactly!

Updating that the solution that worked best is @egonbeermat 4 bit indexed canvas - it both saves RAM usage and works much faster than using a 16 bit canvas, the FPS is great!
Hard for me to mark one single post as a solution, but a combination of a few code blocks made it work.

Thanks again @egonbeermat for all the help to get this up and running!
Here’s a short demo of this working - 800x480px display
Waveform is 800x128px

1 Like