From 876ae491a1c21a6c456c64ee4043730b5cdc104e Mon Sep 17 00:00:00 2001 From: philippe44 Date: Thu, 27 Feb 2020 18:23:00 -0800 Subject: [PATCH 01/20] add 'pause' --- components/display/SSD1306.c | 19 +++++++++++++++++-- components/display/display.c | 3 ++- components/display/display.h | 2 +- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c index 044aca24..1e27fcb7 100644 --- a/components/display/SSD1306.c +++ b/components/display/SSD1306.c @@ -39,6 +39,7 @@ static void Update( struct GDS_Device* Device ) { // not sure the compiler does not have to redo all calculation in for loops, so local it is int width = Device->Width, rows = Device->Height / 8; uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer; + int CurrentRow = -1, FirstCol = -1, LastCol = -1; // by row, find first and last columns that have been updated for (int r = 0; r < rows; r++) { @@ -53,8 +54,22 @@ static void Update( struct GDS_Device* Device ) { // now update the display by "byte rows" if (first--) { - SetColumnAddress( Device, first, last ); - SetPageAddress( Device, r, r); + + // only set column when useful, saves a fair bit of CPU + if (first > FirstCol && first <= FirstCol + 4 && last < LastCol && last >= LastCol - 4) { + first = FirstCol; + last = LastCol; + } else { + SetColumnAddress( Device, first, last ); + FirstCol = first; + LastCol = last; + } + + // Set row only when needed, otherwise let auto-increment work + if (r != CurrentRow) SetPageAddress( Device, r, Device->Height / 8 - 1 ); + CurrentRow = r + 1; + + // actual write Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1); } } diff --git a/components/display/display.c b/components/display/display.c index a1cb0e4d..8741cd7b 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -289,13 +289,14 @@ void displayer_metadata(char *artist, char *album, char *title) { /**************************************************************************************** * */ -void displayer_scroll(char *string, int speed) { +void displayer_scroll(char *string, int speed, int pause) { // need a display! if (!display) return; xSemaphoreTake(displayer.mutex, portMAX_DELAY); if (speed) displayer.speed = speed; + if (pause) displayer.pause = pause; displayer.offset = 0; strncpy(displayer.string, string, SCROLLABLE_SIZE); displayer.string[SCROLLABLE_SIZE] = '\0'; diff --git a/components/display/display.h b/components/display/display.h index 98b2dc92..e1b809e5 100644 --- a/components/display/display.h +++ b/components/display/display.h @@ -42,7 +42,7 @@ enum displayer_time_e { DISPLAYER_ELAPSED, DISPLAYER_REMAINING }; enum display_bus_cmd_e { DISPLAY_BUS_TAKE, DISPLAY_BUS_GIVE }; bool (*display_bus)(void *from, enum display_bus_cmd_e cmd); -void displayer_scroll(char *string, int speed); +void displayer_scroll(char *string, int speed, int pause); void displayer_control(enum displayer_cmd_e cmd, ...); void displayer_metadata(char *artist, char *album, char *title); void displayer_timer(enum displayer_time_e mode, int elapsed, int duration); From 7584b1bd5b9a65db330820021126a195a716f2ee Mon Sep 17 00:00:00 2001 From: philippe44 Date: Thu, 27 Feb 2020 23:05:04 -0800 Subject: [PATCH 02/20] scroller correction + JPEG improvements --- components/display/SH1106.c | 18 +++++++++++------ components/display/SSD1306.c | 21 +++++++++++-------- components/display/SSD132x.c | 21 ++++++++++--------- components/display/core/gds.h | 7 ++++--- components/display/core/gds_image.c | 29 +++++++++++++++++++-------- components/display/core/gds_private.h | 2 +- components/squeezelite/display.c | 16 ++++++++------- 7 files changed, 71 insertions(+), 43 deletions(-) diff --git a/components/display/SH1106.c b/components/display/SH1106.c index f6c1dc2f..f4b93dfe 100644 --- a/components/display/SH1106.c +++ b/components/display/SH1106.c @@ -21,6 +21,10 @@ static char TAG[] = "SH1106"; +struct SH1106_Private { + uint8_t *Shadowbuffer; +}; + // Functions are not declared to minimize # of lines static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) { @@ -36,9 +40,10 @@ static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t En static void Update( struct GDS_Device* Device ) { #ifdef SHADOW_BUFFER + struct SH1106_Private *Private = (struct SH1106_Private*) Device->Private; // not sure the compiler does not have to redo all calculation in for loops, so local it is int width = Device->Width, rows = Device->Height / 8; - uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer; + uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; // by row, find first and last columns that have been updated for (int r = 0; r < rows; r++) { @@ -55,7 +60,7 @@ static void Update( struct GDS_Device* Device ) { if (first--) { SetColumnAddress( Device, first, last ); SetPageAddress( Device, r, r); - Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1); + Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1); } } #else @@ -83,15 +88,16 @@ static bool Init( struct GDS_Device* Device ) { // benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy #ifdef SHADOW_BUFFER + struct SH1106_Private *Private = (struct SH1106_Private*) Device->Private; Device->Framebuffer = calloc( 1, Device->FramebufferSize ); NullCheck( Device->Framebuffer, return false ); #ifdef USE_IRAM - if (Device->IF == IF_SPI) Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); + if (Device->IF == IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); else #endif - Device->Shadowbuffer = malloc( Device->FramebufferSize ); - NullCheck( Device->Shadowbuffer, return false ); - memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize); + Private->Shadowbuffer = malloc( Device->FramebufferSize ); + NullCheck( Private->Shadowbuffer, return false ); + memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize); #else // not SHADOW_BUFFER #ifdef USE_IRAM // benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c index 1e27fcb7..7cea4505 100644 --- a/components/display/SSD1306.c +++ b/components/display/SSD1306.c @@ -21,6 +21,10 @@ static char TAG[] = "SSD1306"; +struct SSD1306_Private { + uint8_t *Shadowbuffer; +}; + // Functions are not deckared to minimize # of lines static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) { @@ -36,9 +40,10 @@ static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t En static void Update( struct GDS_Device* Device ) { #ifdef SHADOW_BUFFER + struct SSD1306_Private *Private = (struct SSD1306_Private*) Device->Private; // not sure the compiler does not have to redo all calculation in for loops, so local it is int width = Device->Width, rows = Device->Height / 8; - uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer; + uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; int CurrentRow = -1, FirstCol = -1, LastCol = -1; // by row, find first and last columns that have been updated @@ -54,7 +59,6 @@ static void Update( struct GDS_Device* Device ) { // now update the display by "byte rows" if (first--) { - // only set column when useful, saves a fair bit of CPU if (first > FirstCol && first <= FirstCol + 4 && last < LastCol && last >= LastCol - 4) { first = FirstCol; @@ -70,7 +74,7 @@ static void Update( struct GDS_Device* Device ) { CurrentRow = r + 1; // actual write - Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1); + Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1); } } #else @@ -94,17 +98,18 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { static bool Init( struct GDS_Device* Device ) { Device->FramebufferSize = ( Device->Width * Device->Height ) / 8; -// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy + // benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy #ifdef SHADOW_BUFFER + struct SSD1306_Private *Private = (struct SSD1306_Private*) Device->Private; Device->Framebuffer = calloc( 1, Device->FramebufferSize ); NullCheck( Device->Framebuffer, return false ); #ifdef USE_IRAM - if (Device->IF == IF_SPI) Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); + if (Device->IF == IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); else #endif - Device->Shadowbuffer = malloc( Device->FramebufferSize ); - NullCheck( Device->Shadowbuffer, return false ); - memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize); + Private->Shadowbuffer = malloc( Device->FramebufferSize ); + NullCheck( Private->Shadowbuffer, return false ); + memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize); #else // not SHADOW_BUFFER #ifdef USE_IRAM // benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy diff --git a/components/display/SSD132x.c b/components/display/SSD132x.c index cdb5779f..cdbadc2b 100644 --- a/components/display/SSD132x.c +++ b/components/display/SSD132x.c @@ -27,7 +27,7 @@ static char TAG[] = "SSD132x"; enum { SSD1326, SSD1327 }; struct SSD132x_Private { - uint8_t *iRAM; + uint8_t *iRAM, *Shadowbuffer; uint8_t ReMap, PageSize; uint8_t Model; }; @@ -73,7 +73,7 @@ static void Update4( struct GDS_Device* Device ) { SetColumnAddress( Device, 0, Device->Width / 2 - 1); #ifdef SHADOW_BUFFER - uint16_t *optr = (uint16_t*) Device->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer; + uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer; bool dirty = false; for (int r = 0, page = 0; r < Device->Height; r++) { @@ -92,10 +92,10 @@ static void Update4( struct GDS_Device* Device ) { SetRowAddress( Device, r - page + 1, r ); // own use of IRAM has not proven to be much better than letting SPI do its copy if (Private->iRAM) { - memcpy(Private->iRAM, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 ); + memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 ); Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 ); } else { - Device->WriteData( Device, Device->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 ); + Device->WriteData( Device, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 ); } dirty = false; } @@ -122,9 +122,10 @@ static void Update4( struct GDS_Device* Device ) { */ static void Update1( struct GDS_Device* Device ) { #ifdef SHADOW_BUFFER + struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private; // not sure the compiler does not have to redo all calculation in for loops, so local it is int width = Device->Width / 8, rows = Device->Height; - uint8_t *optr = Device->Shadowbuffer, *iptr = Device->Framebuffer; + uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; // by row, find first and last columns that have been updated for (int r = 0; r < rows; r++) { @@ -141,7 +142,7 @@ static void Update1( struct GDS_Device* Device ) { if (first--) { SetColumnAddress( Device, first, last ); SetRowAddress( Device, r, r); - Device->WriteData( Device, Device->Shadowbuffer + r*width + first, last - first + 1); + Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1); } } #else @@ -242,15 +243,15 @@ static bool Init( struct GDS_Device* Device ) { #ifdef USE_IRAM if (Device->IF == IF_SPI) { if (Device->Depth == 1) { - Device->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); + Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); } else { - Device->Shadowbuffer = malloc( Device->FramebufferSize ); + Private->Shadowbuffer = malloc( Device->FramebufferSize ); Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); } } else #endif - Device->Shadowbuffer = malloc( Device->FramebufferSize ); - memset(Device->Shadowbuffer, 0xFF, Device->FramebufferSize); + Private->Shadowbuffer = malloc( Device->FramebufferSize ); + memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize); #else // not SHADOW_BUFFER #ifdef USE_IRAM if (Device->IF == IF_SPI) { diff --git a/components/display/core/gds.h b/components/display/core/gds.h index 2c3a9e26..f1b5475c 100644 --- a/components/display/core/gds.h +++ b/components/display/core/gds.h @@ -13,13 +13,14 @@ monochrome mode is not such type of screen, SH1106 and SSD1306 are */ -enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7, +enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1 = 1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7, GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15, + GDS_COLOR_MAX }; #define GDS_COLOR_BLACK GDS_COLOR_L0 -#define GDS_COLOR_WHITE GDS_COLOR_L15 -#define GDS_COLOR_XOR 2 +#define GDS_COLOR_WHITE GDS_COLOR_L1 +#define GDS_COLOR_XOR (GDS_COLOR_MAX + 1) struct GDS_Device; struct GDS_FontDef; diff --git a/components/display/core/gds_image.c b/components/display/core/gds_image.c index e7d4dac7..a5919f74 100644 --- a/components/display/core/gds_image.c +++ b/components/display/core/gds_image.c @@ -21,6 +21,7 @@ typedef struct { struct { // DirectDraw struct GDS_Device * Device; int XOfs, YOfs; + int XMin, YMin; int Depth; }; }; @@ -38,9 +39,7 @@ static unsigned OutHandler(JDEC *Decoder, void *Bitmap, JRECT *Frame) { uint8_t *Pixels = (uint8_t*) Bitmap; for (int y = Frame->top; y <= Frame->bottom; y++) { - if (y < Context->YOfs) continue; for (int x = Frame->left; x <= Frame->right; x++) { - if (x < Context->XOfs) continue; // Convert the 888 to RGB565 uint16_t Value = (*Pixels++ & ~0x07) << 8; Value |= (*Pixels++ & ~0x03) << 3; @@ -57,12 +56,14 @@ static unsigned OutHandlerDirect(JDEC *Decoder, void *Bitmap, JRECT *Frame) { int Shift = 8 - Context->Depth; for (int y = Frame->top; y <= Frame->bottom; y++) { + if (y < Context->YMin) continue; for (int x = Frame->left; x <= Frame->right; x++) { + if (x < Context->XMin) continue; // Convert the 888 to RGB565 int Value = ((Pixels[0]*11 + Pixels[1]*59 + Pixels[2]*30) / 100) >> Shift; Pixels += 3; // used DrawPixel and not "fast" version as X,Y may be beyond screen - GDS_DrawPixel( Context->Device, Context->XOfs + x, Context->YOfs + y, Value); + GDS_DrawPixel( Context->Device, x + Context->XOfs, y + Context->YOfs, Value); } } return 1; @@ -90,11 +91,16 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal Decoder.scale = Scale; if (Res == JDR_OK && !SizeOnly) { - // find the scaling factor Context.OutData = malloc(Decoder.width * Decoder.height * sizeof(uint16_t)); + + // find the scaling factor uint8_t N = 0, ScaleInt = ceil(1.0 / Scale); ScaleInt--; ScaleInt |= ScaleInt >> 1; ScaleInt |= ScaleInt >> 2; ScaleInt++; while (ScaleInt >>= 1) N++; + if (N > 3) { + ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height); + N = 3; + } // ready to decode if (Context.OutData) { @@ -102,7 +108,7 @@ static uint16_t* DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scal Context.Height = Decoder.height / (1 << N); if (Width) *Width = Context.Width; if (Height) *Height = Context.Height; - Res = jd_decomp(&Decoder, OutHandler, N > 3 ? 3 : N); + Res = jd_decomp(&Decoder, OutHandler, N); if (Res != JDR_OK) { ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res); } @@ -202,16 +208,23 @@ bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int uint8_t Ratio = XRatio < YRatio ? ceil(1/XRatio) : ceil(1/YRatio); Ratio--; Ratio |= Ratio >> 1; Ratio |= Ratio >> 2; Ratio++; while (Ratio >>= 1) N++; + if (N > 3) { + ESP_LOGW(TAG, "Image will not fit %dx%d", Decoder.width, Decoder.height); + N = 3; + } Context.Width /= 1 << N; Context.Height /= 1 << N; } // then place it - if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = x + ((Device->Width - x) - Context.Width) / 2; - if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = y + ((Device->Height - y) - Context.Height) / 2; + if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = (Device->Width + x - Context.Width) / 2; + if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = (Device->Height + y - Context.Height) / 2; + Context.XMin = x - Context.XOfs; + Context.YMin = y - Context.YOfs; + // do decompress & draw - Res = jd_decomp(&Decoder, OutHandlerDirect, N > 3 ? 3 : N); + Res = jd_decomp(&Decoder, OutHandlerDirect, N); if (Res == JDR_OK) { Ret = true; } else { diff --git a/components/display/core/gds_private.h b/components/display/core/gds_private.h index 60893be5..76fcb167 100644 --- a/components/display/core/gds_private.h +++ b/components/display/core/gds_private.h @@ -83,7 +83,7 @@ struct GDS_Device { uint16_t Height; uint8_t Depth; - uint8_t* Framebuffer, *Shadowbuffer; + uint8_t* Framebuffer; uint16_t FramebufferSize; bool Dirty; diff --git a/components/squeezelite/display.c b/components/squeezelite/display.c index f7c9b385..91030330 100644 --- a/components/squeezelite/display.c +++ b/components/squeezelite/display.c @@ -115,7 +115,7 @@ static struct scroller_s { u16_t mode; s16_t by; // scroller management & sharing between grfg and scrolling task - bool active, first; + bool active, first, overflow; int scrolled; struct { u8_t *frame; @@ -234,7 +234,7 @@ bool sb_display_init(void) { displayer.task = xTaskCreateStatic( (TaskFunction_t) displayer_task, "displayer_thread", SCROLL_STACK_SIZE, NULL, ESP_TASK_PRIO_MIN + 1, xStack, &xTaskBuffer); // size scroller (width + current screen) - scroller.scroll.max = (displayer.width * displayer.height / 8) * (10 + 1); + scroller.scroll.max = (displayer.width * displayer.height / 8) * (15 + 1); scroller.scroll.frame = malloc(scroller.scroll.max); scroller.back.frame = malloc(displayer.width * displayer.height / 8); scroller.frame = malloc(displayer.width * displayer.height / 8); @@ -559,6 +559,7 @@ static void grfs_handler(u8_t *data, int len) { scroller.mode = htons(pkt->mode); scroller.scroll.width = htons(pkt->width); scroller.first = true; + scroller.overflow = false; // background excludes space taken by visu (if any) scroller.back.width = displayer.width - ((visu.mode && visu.row < SB_HEIGHT) ? visu.width : 0); @@ -576,13 +577,14 @@ static void grfs_handler(u8_t *data, int len) { } // copy scroll frame data (no semaphore needed) - if (scroller.scroll.size + size < scroller.scroll.max) { + if (scroller.scroll.size + size < scroller.scroll.max && !scroller.overflow) { memcpy(scroller.scroll.frame + offset, data + sizeof(struct grfs_packet), size); scroller.scroll.size = offset + size; - LOG_INFO("scroller current size %u", scroller.scroll.size); + LOG_INFO("scroller current size %u (w:%u)", scroller.scroll.size, scroller.scroll.width); } else { - LOG_INFO("scroller too larger %u/%u/%u", scroller.scroll.size + size, scroller.scroll.max, scroller.scroll.width); - scroller.scroll.width = scroller.scroll.size / (displayer.height / 8); + LOG_INFO("scroller too large %u/%u (w:%u)", scroller.scroll.size + size, scroller.scroll.max, scroller.scroll.width); + scroller.scroll.width = scroller.scroll.size / (displayer.height / 8) - scroller.back.width; + scroller.overflow = true; } } @@ -860,7 +862,7 @@ static void displayer_task(void *args) { // by default go for the long sleep, will change below if required scroller.wake = LONG_WAKE; - // do we have more to scroll (scroll.width is the last column from which we havea full zone) + // do we have more to scroll (scroll.width is the last column from which we have a full zone) if (scroller.by > 0 ? (scroller.scrolled <= scroller.scroll.width) : (scroller.scrolled >= 0)) { memcpy(scroller.frame, scroller.back.frame, scroller.back.width * displayer.height / 8); for (int i = 0; i < scroller.width * displayer.height / 8; i++) scroller.frame[i] |= scroller.scroll.frame[scroller.scrolled * displayer.height / 8 + i]; From 04f74b310dd756e98f9d5be2598cdc7cb455c03f Mon Sep 17 00:00:00 2001 From: philippe44 Date: Thu, 27 Feb 2020 23:07:27 -0800 Subject: [PATCH 03/20] typos - release --- components/display/SSD1306.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c index 7cea4505..eada1e12 100644 --- a/components/display/SSD1306.c +++ b/components/display/SSD1306.c @@ -25,7 +25,7 @@ struct SSD1306_Private { uint8_t *Shadowbuffer; }; -// Functions are not deckared to minimize # of lines +// Functions are not declared to minimize # of lines static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) { Device->WriteCommand( Device, 0x21 ); From 4423fa2ad3b8647ae8c2397eb937bf0542d98697 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Fri, 28 Feb 2020 10:30:11 -0800 Subject: [PATCH 04/20] y line draw boundary --- components/display/core/gds_draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/display/core/gds_draw.c b/components/display/core/gds_draw.c index 60467a33..7e6995e0 100644 --- a/components/display/core/gds_draw.c +++ b/components/display/core/gds_draw.c @@ -58,7 +58,7 @@ void GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Colo if (XEnd >= Device->Width) XEnd = Device->Width - 1; if (y < 0) y = 0; - else if (y >= Device->Height) x = Device->Height - 1; + else if (y >= Device->Height) y = Device->Height - 1; for ( ; x < XEnd; x++ ) GDS_DrawPixelFast( Device, x, y, Color ); } From efa818e7949e615c6c3da6e5a4926f27eb0e42e6 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Fri, 28 Feb 2020 20:56:49 -0800 Subject: [PATCH 05/20] Display improvements --- components/display/SH1106.c | 25 +++------ components/display/SSD1306.c | 25 +++------ components/display/SSD132x.c | 51 +++++++------------ components/display/core/gds.c | 22 ++++++-- components/display/core/gds.h | 2 +- components/display/core/gds_private.h | 16 ++++-- .../display/core/ifaces/default_if_i2c.c | 4 +- .../display/core/ifaces/default_if_spi.c | 4 +- components/display/display.c | 4 +- 9 files changed, 67 insertions(+), 86 deletions(-) diff --git a/components/display/SH1106.c b/components/display/SH1106.c index f4b93dfe..ccdc36b8 100644 --- a/components/display/SH1106.c +++ b/components/display/SH1106.c @@ -21,7 +21,7 @@ static char TAG[] = "SH1106"; -struct SH1106_Private { +struct PrivateSpace { uint8_t *Shadowbuffer; }; @@ -40,7 +40,7 @@ static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t En static void Update( struct GDS_Device* Device ) { #ifdef SHADOW_BUFFER - struct SH1106_Private *Private = (struct SH1106_Private*) Device->Private; + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; // not sure the compiler does not have to redo all calculation in for loops, so local it is int width = Device->Width, rows = Device->Height / 8; uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; @@ -84,27 +84,15 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { } static bool Init( struct GDS_Device* Device ) { - Device->FramebufferSize = ( Device->Width * Device->Height ) / 8; - -// benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy #ifdef SHADOW_BUFFER - struct SH1106_Private *Private = (struct SH1106_Private*) Device->Private; - Device->Framebuffer = calloc( 1, Device->FramebufferSize ); - NullCheck( Device->Framebuffer, return false ); + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; #ifdef USE_IRAM - if (Device->IF == IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); + if (Device->IF == GDS_IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); else #endif Private->Shadowbuffer = malloc( Device->FramebufferSize ); NullCheck( Private->Shadowbuffer, return false ); memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize); -#else // not SHADOW_BUFFER -#ifdef USE_IRAM - // benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy - if (Device->IF == IF_SPI) Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); - else -#endif - Device->Framebuffer = calloc( 1, Device->FramebufferSize ); #endif // need to be off and disable display RAM @@ -149,8 +137,6 @@ static const struct GDS_Device SH1106 = { .DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast, .SetVFlip = SetVFlip, .SetHFlip = SetHFlip, .Update = Update, .Init = Init, - //.DrawPixelFast = GDS_DrawPixelFast, - //.ClearWindow = ClearWindow, }; struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) { @@ -159,6 +145,9 @@ struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) { if (!Device) Device = calloc(1, sizeof(struct GDS_Device)); *Device = SH1106; Device->Depth = 1; +#if !defined SHADOW_BUFFER && defined USE_IRAM + Device->Alloc = GDS_ALLOC_IRAM_SPI; +#endif ESP_LOGI(TAG, "SH1106 driver"); return Device; diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c index eada1e12..faaacea2 100644 --- a/components/display/SSD1306.c +++ b/components/display/SSD1306.c @@ -21,7 +21,7 @@ static char TAG[] = "SSD1306"; -struct SSD1306_Private { +struct PrivateSpace { uint8_t *Shadowbuffer; }; @@ -40,7 +40,7 @@ static void SetPageAddress( struct GDS_Device* Device, uint8_t Start, uint8_t En static void Update( struct GDS_Device* Device ) { #ifdef SHADOW_BUFFER - struct SSD1306_Private *Private = (struct SSD1306_Private*) Device->Private; + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; // not sure the compiler does not have to redo all calculation in for loops, so local it is int width = Device->Width, rows = Device->Height / 8; uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; @@ -96,27 +96,15 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { } static bool Init( struct GDS_Device* Device ) { - Device->FramebufferSize = ( Device->Width * Device->Height ) / 8; - - // benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy #ifdef SHADOW_BUFFER - struct SSD1306_Private *Private = (struct SSD1306_Private*) Device->Private; - Device->Framebuffer = calloc( 1, Device->FramebufferSize ); - NullCheck( Device->Framebuffer, return false ); + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; #ifdef USE_IRAM - if (Device->IF == IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); + if (Device->IF == GDS_IF_SPI) Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); else #endif Private->Shadowbuffer = malloc( Device->FramebufferSize ); NullCheck( Private->Shadowbuffer, return false ); memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize); -#else // not SHADOW_BUFFER -#ifdef USE_IRAM - // benchmarks showed little gain to have SPI memory already in IRAL vs letting driver copy - if (Device->IF == IF_SPI) Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); - else -#endif - Device->Framebuffer = calloc( 1, Device->FramebufferSize ); #endif // need to be off and disable display RAM @@ -164,8 +152,6 @@ static const struct GDS_Device SSD1306 = { .DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast, .SetVFlip = SetVFlip, .SetHFlip = SetHFlip, .Update = Update, .Init = Init, - //.DrawPixelFast = GDS_DrawPixelFast, - //.ClearWindow = ClearWindow, }; struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device) { @@ -174,6 +160,9 @@ struct GDS_Device* SSD1306_Detect(char *Driver, struct GDS_Device* Device) { if (!Device) Device = calloc(1, sizeof(struct GDS_Device)); *Device = SSD1306; Device->Depth = 1; +#if !defined SHADOW_BUFFER && defined USE_IRAM + Device->Alloc = GDS_ALLOC_IRAM_SPI; +#endif ESP_LOGI(TAG, "SSD1306 driver"); return Device; diff --git a/components/display/SSD132x.c b/components/display/SSD132x.c index cdbadc2b..217c51c9 100644 --- a/components/display/SSD132x.c +++ b/components/display/SSD132x.c @@ -26,7 +26,7 @@ static char TAG[] = "SSD132x"; enum { SSD1326, SSD1327 }; -struct SSD132x_Private { +struct PrivateSpace { uint8_t *iRAM, *Shadowbuffer; uint8_t ReMap, PageSize; uint8_t Model; @@ -67,7 +67,7 @@ static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End } static void Update4( struct GDS_Device* Device ) { - struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private; + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; // always update by full lines SetColumnAddress( Device, 0, Device->Width / 2 - 1); @@ -122,7 +122,7 @@ static void Update4( struct GDS_Device* Device ) { */ static void Update1( struct GDS_Device* Device ) { #ifdef SHADOW_BUFFER - struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private; + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; // not sure the compiler does not have to redo all calculation in for loops, so local it is int width = Device->Width / 8, rows = Device->Height; uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; @@ -198,7 +198,7 @@ static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, i } static void SetHFlip( struct GDS_Device* Device, bool On ) { - struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private; + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; if (Private->Model == SSD1326) Private->ReMap = On ? (Private->ReMap | ((1 << 0) | (1 << 2))) : (Private->ReMap & ~((1 << 0) | (1 << 2))); else Private->ReMap = On ? (Private->ReMap | ((1 << 0) | (1 << 1))) : (Private->ReMap & ~((1 << 0) | (1 << 1))); Device->WriteCommand( Device, 0xA0 ); @@ -206,7 +206,7 @@ static void SetHFlip( struct GDS_Device* Device, bool On ) { } static void SetVFlip( struct GDS_Device *Device, bool On ) { - struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private; + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; if (Private->Model == SSD1326) Private->ReMap = On ? (Private->ReMap | (1 << 1)) : (Private->ReMap & ~(1 << 1)); else Private->ReMap = On ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4)); Device->WriteCommand( Device, 0xA0 ); @@ -222,26 +222,15 @@ static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { } static bool Init( struct GDS_Device* Device ) { - struct SSD132x_Private *Private = (struct SSD132x_Private*) Device->Private; + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; // find a page size that is not too small is an integer of height Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2)); Private->PageSize = Device->Height / (Device->Height / Private->PageSize) ; -#ifdef USE_IRAM - // let SPI driver allocate memory, it has not proven to be more efficient - if (Device->IF == IF_SPI) Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); -#endif - Device->FramebufferSize = ( Device->Width * Device->Height ) / 2; - Device->Framebuffer = calloc( 1, Device->FramebufferSize ); - NullCheck( Device->Framebuffer, return false ); - -// benchmarks showed little gain to have SPI memory already in IRAM vs letting driver copy #ifdef SHADOW_BUFFER - Device->Framebuffer = calloc( 1, Device->FramebufferSize ); - NullCheck( Device->Framebuffer, return false ); #ifdef USE_IRAM - if (Device->IF == IF_SPI) { + if (Device->IF == GDS_IF_SPI) { if (Device->Depth == 1) { Private->Shadowbuffer = heap_caps_malloc( Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); } else { @@ -252,20 +241,13 @@ static bool Init( struct GDS_Device* Device ) { #endif Private->Shadowbuffer = malloc( Device->FramebufferSize ); memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize); -#else // not SHADOW_BUFFER -#ifdef USE_IRAM - if (Device->IF == IF_SPI) { - if (Device->Depth == 1) { - Device->Framebuffer = heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); - } else { - Device->Framebuffer = calloc( 1, Device->FramebufferSize ); - Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); - } - } else -#endif - Device->Framebuffer = calloc( 1, Device->FramebufferSize ); +#else +#ifdef USE_IRAM + if (Device->Depth == 4 && Device->IF == GDS_IF_SPI) Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); #endif +#endif + ESP_LOGI(TAG, "SSD1326/7 with bit depth %u, page %u, iRAM %p", Device->Depth, Private->PageSize, Private->iRAM); // need to be off and disable display RAM @@ -317,14 +299,14 @@ static const struct GDS_Device SSD132x = { struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) { uint8_t Model; - if (!strcasestr(Driver, "SSD1326")) Model = SSD1326; - else if (!strcasestr(Driver, "SSD1327")) Model = SSD1327; + if (strcasestr(Driver, "SSD1326")) Model = SSD1326; + else if (strcasestr(Driver, "SSD1327")) Model = SSD1327; else return NULL; if (!Device) Device = calloc(1, sizeof(struct GDS_Device)); *Device = SSD132x; - ((struct SSD132x_Private*) Device->Private)->Model = Model; + ((struct PrivateSpace*) Device->Private)->Model = Model; sscanf(Driver, "%*[^:]:%c", &Device->Depth); @@ -333,6 +315,9 @@ struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) { Device->DrawPixelFast = DrawPixel1Fast; Device->DrawBitmapCBR = DrawBitmapCBR; Device->ClearWindow = ClearWindow; +#if !defined SHADOW_BUFFER && defined USE_IRAM + Device->Alloc = GDS_ALLOC_IRAM_SPI; +#endif } else { Device->Depth = 4; } diff --git a/components/display/core/gds.c b/components/display/core/gds.c index 7f59f79e..72567ba4 100644 --- a/components/display/core/gds.c +++ b/components/display/core/gds.c @@ -132,10 +132,22 @@ bool GDS_Reset( struct GDS_Device* Device ) { return true; } -void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { Device->SetContrast( Device, Contrast); } -void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { Device->SetHFlip( Device, On ); } -void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { Device->SetVFlip( Device, On ); } +bool GDS_Init( struct GDS_Device* Device ) { + Device->FramebufferSize = ( Device->Width * Device->Height ) / (8 / Device->Depth); + + if ((Device->Alloc && GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); + else Device->Framebuffer = calloc( 1, Device->FramebufferSize ); + NullCheck( Device->Framebuffer, return false ); + + bool Res = Device->Init( Device ); + if (!Res) free(Device->Framebuffer); + return Res; +} + +void GDS_SetContrast( struct GDS_Device* Device, uint8_t Contrast ) { if (Device->SetContrast) Device->SetContrast( Device, Contrast); } +void GDS_SetHFlip( struct GDS_Device* Device, bool On ) { if (Device->SetHFlip) Device->SetHFlip( Device, On ); } +void GDS_SetVFlip( struct GDS_Device* Device, bool On ) { if (Device->SetVFlip) Device->SetVFlip( Device, On ); } int GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; } int GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; } -void GDS_DisplayOn( struct GDS_Device* Device ) { Device->DisplayOn( Device ); } -void GDS_DisplayOff( struct GDS_Device* Device ) { Device->DisplayOff( Device ); } \ No newline at end of file +void GDS_DisplayOn( struct GDS_Device* Device ) { if (Device->DisplayOn) Device->DisplayOn( Device ); } +void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); } \ No newline at end of file diff --git a/components/display/core/gds.h b/components/display/core/gds.h index f1b5475c..d0ec7ef9 100644 --- a/components/display/core/gds.h +++ b/components/display/core/gds.h @@ -19,7 +19,7 @@ enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1 = 1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLO }; #define GDS_COLOR_BLACK GDS_COLOR_L0 -#define GDS_COLOR_WHITE GDS_COLOR_L1 +#define GDS_COLOR_WHITE (GDS_COLOR_MAX - 1) #define GDS_COLOR_XOR (GDS_COLOR_MAX + 1) struct GDS_Device; diff --git a/components/display/core/gds_private.h b/components/display/core/gds_private.h index 76fcb167..74fd8630 100644 --- a/components/display/core/gds_private.h +++ b/components/display/core/gds_private.h @@ -7,6 +7,9 @@ #include "gds.h" #include "gds_err.h" +#define GDS_ALLOC_IRAM 0x01 +#define GDS_ALLOC_IRAM_SPI 0x02 + #define GDS_CLIPDEBUG_NONE 0 #define GDS_CLIPDEBUG_WARNING 1 #define GDS_CLIPDEBUG_ERROR 2 @@ -55,8 +58,8 @@ typedef bool ( *WriteDataProc ) ( struct GDS_Device* Device, const uint8_t* Data struct spi_device_t; typedef struct spi_device_t* spi_device_handle_t; -#define IF_SPI 0 -#define IF_I2C 1 +#define GDS_IF_SPI 0 +#define GDS_IF_I2C 1 struct GDS_Device { uint8_t IF; @@ -82,7 +85,8 @@ struct GDS_Device { uint16_t Width; uint16_t Height; uint8_t Depth; - + + uint8_t Alloc; uint8_t* Framebuffer; uint16_t FramebufferSize; bool Dirty; @@ -95,12 +99,13 @@ struct GDS_Device { // various driver-specific method // must always provide bool (*Init)( struct GDS_Device* Device); + void (*Update)( struct GDS_Device* Device ); + // may provide if supported void (*SetContrast)( struct GDS_Device* Device, uint8_t Contrast ); void (*DisplayOn)( struct GDS_Device* Device ); void (*DisplayOff)( struct GDS_Device* Device ); void (*SetHFlip)( struct GDS_Device* Device, bool On ); void (*SetVFlip)( struct GDS_Device* Device, bool On ); - void (*Update)( struct GDS_Device* Device ); // must provide for depth other than 1 (vertical) and 4 (may provide for optimization) void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color ); void (*DrawBitmapCBR)(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ); @@ -117,6 +122,7 @@ struct GDS_Device { }; bool GDS_Reset( struct GDS_Device* Device ); +bool GDS_Init( struct GDS_Device* Device ); inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) { bool Result = ( @@ -152,7 +158,7 @@ inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int X, int if ( Color == GDS_COLOR_XOR ) { *FBOffset ^= BIT( YBit ); } else { - *FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit ); + *FBOffset = ( Color >= GDS_COLOR_WHITE / 2 ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit ); } } diff --git a/components/display/core/ifaces/default_if_i2c.c b/components/display/core/ifaces/default_if_i2c.c index c4b91b16..a7545b45 100644 --- a/components/display/core/ifaces/default_if_i2c.c +++ b/components/display/core/ifaces/default_if_i2c.c @@ -73,7 +73,7 @@ bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int Device->WriteData = I2CDefaultWriteData; Device->Address = I2CAddress; Device->RSTPin = RSTPin; - Device->IF = IF_I2C; + Device->IF = GDS_IF_I2C; Device->Width = Width; Device->Height = Height; @@ -83,7 +83,7 @@ bool GDS_I2CAttachDevice( struct GDS_Device* Device, int Width, int Height, int GDS_Reset( Device ); } - return Device->Init( Device ); + return GDS_Init( Device ); } static bool I2CDefaultWriteBytes( int Address, bool IsCommand, const uint8_t* Data, size_t DataLength ) { diff --git a/components/display/core/ifaces/default_if_spi.c b/components/display/core/ifaces/default_if_spi.c index 0bfb7c4e..7ab48e7a 100644 --- a/components/display/core/ifaces/default_if_spi.c +++ b/components/display/core/ifaces/default_if_spi.c @@ -58,7 +58,7 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int Device->SPIHandle = SPIDevice; Device->RSTPin = RSTPin; Device->CSPin = CSPin; - Device->IF = IF_SPI; + Device->IF = GDS_IF_SPI; Device->Width = Width; Device->Height = Height; @@ -68,7 +68,7 @@ bool GDS_SPIAttachDevice( struct GDS_Device* Device, int Width, int Height, int GDS_Reset( Device ); } - return Device->Init( Device ); + return GDS_Init( Device ); } static bool SPIDefaultWriteBytes( spi_device_handle_t SPIHandle, int WriteMode, const uint8_t* Data, size_t DataLength ) { diff --git a/components/display/display.c b/components/display/display.c index 8741cd7b..bcdc505b 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -59,8 +59,8 @@ static EXT_RAM_ATTR struct { static void displayer_task(void *args); struct GDS_Device *display; -extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect; -GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, NULL }; +extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, ELD_Detect; +GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, ELD_Detect, NULL }; /**************************************************************************************** * From 2d0eac5aee4a7abebb8c5a0ac80b18151ac2f1c2 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Fri, 28 Feb 2020 20:58:29 -0800 Subject: [PATCH 06/20] Remove ELD reference --- components/display/display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/display/display.c b/components/display/display.c index bcdc505b..8741cd7b 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -59,8 +59,8 @@ static EXT_RAM_ATTR struct { static void displayer_task(void *args); struct GDS_Device *display; -extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, ELD_Detect; -GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, ELD_Detect, NULL }; +extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect; +GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, NULL }; /**************************************************************************************** * From 9759c0dbef0e6688d5570a8c2cd267c128821cc8 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sat, 29 Feb 2020 22:23:34 -0800 Subject: [PATCH 07/20] adding SSD1675 driver --- components/display/SH1106.c | 2 +- components/display/SSD1306.c | 14 +- components/display/SSD132x.c | 8 +- components/display/SSD1675.c | 252 ++++++++++++++++++++++++++ components/display/core/gds.c | 15 +- components/display/core/gds.h | 6 +- components/display/core/gds_private.h | 5 +- components/display/display.c | 13 +- 8 files changed, 287 insertions(+), 28 deletions(-) create mode 100644 components/display/SSD1675.c diff --git a/components/display/SH1106.c b/components/display/SH1106.c index ccdc36b8..91e33a54 100644 --- a/components/display/SH1106.c +++ b/components/display/SH1106.c @@ -143,7 +143,7 @@ struct GDS_Device* SH1106_Detect(char *Driver, struct GDS_Device* Device) { if (!strcasestr(Driver, "SH1106")) return NULL; if (!Device) Device = calloc(1, sizeof(struct GDS_Device)); - *Device = SH1106; + *Device = SH1106; Device->Depth = 1; #if !defined SHADOW_BUFFER && defined USE_IRAM Device->Alloc = GDS_ALLOC_IRAM_SPI; diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c index faaacea2..fec88996 100644 --- a/components/display/SSD1306.c +++ b/components/display/SSD1306.c @@ -42,12 +42,12 @@ static void Update( struct GDS_Device* Device ) { #ifdef SHADOW_BUFFER struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; // not sure the compiler does not have to redo all calculation in for loops, so local it is - int width = Device->Width, rows = Device->Height / 8; + int width = Device->Width, pages = Device->Height / 8; uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; - int CurrentRow = -1, FirstCol = -1, LastCol = -1; + int CurrentPage = -1, FirstCol = -1, LastCol = -1; // by row, find first and last columns that have been updated - for (int r = 0; r < rows; r++) { + for (int p = 0; p < pages; p++) { uint8_t first = 0, last; for (int c = 0; c < width; c++) { if (*iptr != *optr) { @@ -70,15 +70,15 @@ static void Update( struct GDS_Device* Device ) { } // Set row only when needed, otherwise let auto-increment work - if (r != CurrentRow) SetPageAddress( Device, r, Device->Height / 8 - 1 ); - CurrentRow = r + 1; + if (p != CurrentPage) SetPageAddress( Device, p, Device->Height / 8 - 1 ); + CurrentPage = p + 1; // actual write - Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1); + Device->WriteData( Device, Private->Shadowbuffer + p*width + first, last - first + 1); } } #else - // automatic counter and end Page/Column + // automatic counter and end Page/Column (we assume Height % 8 == 0) SetColumnAddress( Device, 0, Device->Width - 1); SetPageAddress( Device, 0, Device->Height / 8 - 1); Device->WriteData( Device, Device->Framebuffer, Device->FramebufferSize ); diff --git a/components/display/SSD132x.c b/components/display/SSD132x.c index 217c51c9..6088d187 100644 --- a/components/display/SSD132x.c +++ b/components/display/SSD132x.c @@ -162,7 +162,7 @@ static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, i *FBOffset ^= BIT( 7 - XBit ); } else { // we might be able to save the 7-Xbit using BitRemap (A0 bit 2) - *FBOffset = ( Color == GDS_COLOR_WHITE ) ? *FBOffset | BIT( 7 - XBit ) : *FBOffset & ~BIT( 7 - XBit ); + *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( 7 - XBit ) : *FBOffset | BIT( 7 - XBit ); } } @@ -181,8 +181,6 @@ static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int c += chunk * 8; while (c <= x2) DrawPixel1Fast( Device, c++, r, Color ); } - - Device->Dirty = true; } static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) { @@ -193,8 +191,6 @@ static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, i // just do bitreverse and if BitRemap works, there will be even nothing to do for (int i = Height * Width >> 3; --i >= 0;) *optr++ = BitReverseTable256[*Data++]; - - // Dirty is set for us } static void SetHFlip( struct GDS_Device* Device, bool On ) { @@ -307,7 +303,7 @@ struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) { *Device = SSD132x; ((struct PrivateSpace*) Device->Private)->Model = Model; - + sscanf(Driver, "%*[^:]:%c", &Device->Depth); if (Model == SSD1326 && Device->Depth == 1) { diff --git a/components/display/SSD1675.c b/components/display/SSD1675.c new file mode 100644 index 00000000..390c8986 --- /dev/null +++ b/components/display/SSD1675.c @@ -0,0 +1,252 @@ +/** + * Copyright (c) 2017-2018 Tara Keeling + * 2020 Philippe G. + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include + +#include "gds.h" +#include "gds_private.h" + +static char TAG[] = "SSD1675"; + +const unsigned char EPD_lut_full_update[] = { + 0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7 + 0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7 + 0x80,0x60,0x40,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7 + 0x10,0x60,0x20,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7 + 0x03,0x03,0x00,0x00,0x02, // TP0 A~D RP0 + 0x09,0x09,0x00,0x00,0x02, // TP1 A~D RP1 + 0x03,0x03,0x00,0x00,0x02, // TP2 A~D RP2 + 0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3 + 0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4 + 0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5 + 0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6 + 0x15,0x41,0xA8,0x32,0x30,0x0A, +}; + +const unsigned char EPD_lut_partial_update[]= { //20 bytes + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT0: BB: VS 0 ~7 + 0x80,0x00,0x00,0x00,0x00,0x00,0x00, //LUT1: BW: VS 0 ~7 + 0x40,0x00,0x00,0x00,0x00,0x00,0x00, //LUT2: WB: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT3: WW: VS 0 ~7 + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, //LUT4: VCOM: VS 0 ~7 + 0x0A,0x00,0x00,0x00,0x00, // TP0 A~D RP0 + 0x00,0x00,0x00,0x00,0x00, // TP1 A~D RP1 + 0x00,0x00,0x00,0x00,0x00, // TP2 A~D RP2 + 0x00,0x00,0x00,0x00,0x00, // TP3 A~D RP3 + 0x00,0x00,0x00,0x00,0x00, // TP4 A~D RP4 + 0x00,0x00,0x00,0x00,0x00, // TP5 A~D RP5 + 0x00,0x00,0x00,0x00,0x00, // TP6 A~D RP6 + 0x15,0x41,0xA8,0x32,0x30,0x0A, +}; + +struct PrivateSpace { + int ReadyPin; + uint16_t Height; +}; + +// Functions are not declared to minimize # of lines + +void WaitReady( struct GDS_Device* Device) { + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; + if (Private->ReadyPin >= 0) { + int count = 4*1000; + while (gpio_get_level( Private->ReadyPin ) && count) { + vTaskDelay( pdMS_TO_TICKS(100) ); + count -= 100; + } + } else { + vTaskDelay( pdMS_TO_TICKS(2000) ); + } +} + +static void WriteByte( struct GDS_Device* Device, uint8_t Data ) { + Device->WriteData( Device, &Data, 1 ); +} + +static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) { + // start might be greater than end if we decrement + Device->WriteCommand( Device, 0x44 ); + Device->WriteData( Device, &Start, 1 ); + Device->WriteData( Device, &End, 1 ); + + // we obviously want to start ... from the start + Device->WriteCommand( Device, 0x4e ); + WriteByte( Device, Start ); +} +static void SetRowAddress( struct GDS_Device* Device, uint16_t Start, uint16_t End ) { + // start might be greater than end if we decrement + Device->WriteCommand( Device, 0x45 ); + WriteByte( Device, Start ); + WriteByte( Device, Start >> 8 ); + WriteByte( Device, End ); + WriteByte( Device, End >> 8 ); + + // we obviously want to start ... from the start + Device->WriteCommand( Device, 0x4f ); + WriteByte( Device, Start ); + WriteByte( Device, Start >> 8 ); +} + +static void Update( struct GDS_Device* Device ) { + uint8_t *iptr = Device->Framebuffer; + + Device->WriteCommand( Device, 0x24 ); + + // this is awfully slow, but e-ink are slow anyway ... + for (int i = Device->FramebufferSize; --i >= 0;) { + WriteByte( Device, ~*iptr++ ); + } + + Device->WriteCommand( Device, 0x22 ); + WriteByte( Device, 0xC7); + Device->WriteCommand( Device, 0X20 ); + + WaitReady( Device ); +} + +// remember that for these ELD drivers W and H are "inverted" +static void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) { + uint32_t YBit = ( Y & 0x07 ); + Y>>= 3; + + uint8_t* FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X ); + *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( 7-YBit ) : *FBOffset | BIT( 7-YBit ); +} + +static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) { + for (int r = y1; r <= y2; r++) { + for (int c = x1; c <= x2; c++) { + DrawPixelFast( Device, c, r, Color ); + } + } +} + +static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) { + if (!Height) Height = Device->Height; + if (!Width) Width = Device->Width; + + // just do row/column swap + for (int r = 0; r < Height; r++) { + uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r; + for (int c = Width; --c >= 0;) { + *optr++ = *iptr; + iptr += Height; + } + } +} + +static bool Init( struct GDS_Device* Device ) { + struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private; + + // need to re-adjust framebuffer because these are non % 8 + Private->Height = Device->Height; + if (Device->Height & 0x07) Device->Height = ((Device->Height >> 3) + 1) << 3; + + Device->FramebufferSize = Device->Width * Device->Height / 8; + Device->Framebuffer = calloc(1, Device->FramebufferSize); + NullCheck( Device->Framebuffer, return false ); + + if (Private->ReadyPin >= 0) { + gpio_pad_select_gpio( Private->ReadyPin ); + gpio_set_pull_mode( Private->ReadyPin, GPIO_PULLUP_ONLY); + gpio_set_direction( Private->ReadyPin, GPIO_MODE_INPUT ); + } + + // soft reset + vTaskDelay(pdMS_TO_TICKS( 2000 )); + Device->WriteCommand( Device, 0x12 ); + WaitReady( Device ); + + Device->WriteCommand( Device, 0x74 ); + WriteByte( Device, 0x54 ); + Device->WriteCommand( Device, 0x7e ); + WriteByte( Device, 0x3B ); + + Device->WriteCommand( Device, 0x3c ); + WriteByte( Device, 0x03 ); + + Device->WriteCommand( Device, 0x2c ); + WriteByte( Device, 0x55 ); + + Device->WriteCommand( Device, 0x03 ); + WriteByte( Device, EPD_lut_full_update[70] ); + + Device->WriteCommand( Device, 0x04 ); + WriteByte( Device, EPD_lut_full_update[71] ); + WriteByte( Device, EPD_lut_full_update[72] ); + WriteByte( Device, EPD_lut_full_update[73] ); + + Device->WriteCommand( Device, 0x3a ); + WriteByte( Device, EPD_lut_full_update[74] ); + Device->WriteCommand( Device, 0x3b ); + WriteByte( Device, EPD_lut_full_update[75] ); + + Device->WriteCommand( Device, 0X32 ); + for (int i = 0; i < 70; i++) { + WriteByte( Device, EPD_lut_full_update[i] ); + } + + // now deal with funny X/Y layout (W and H are "inverted") + Device->WriteCommand( Device, 0x01 ); + WriteByte( Device, Device->Width - 1 ); + WriteByte( Device, (Device->Width - 1) >> 8 ); + WriteByte( Device, (0 << 0) ); + + /* + Start from 0, Ymax, incX, decY. Starting from X=Height would be difficult + as we would hit the extra bits added because height % 8 != 0 and they are + not written by the DrawPixel. By starting from X=0 we are aligned and + doing incY is like a clockwise 90° rotation (vs otherwise we would virtually + do a counter-clockwise rotation but again have the "border" issue. + */ + Device->WriteCommand( Device, 0x11 ); + WriteByte( Device, (1 << 2) | (0 << 1) | (1 << 0)); + + // must be in order with increment/decrement i.e start might be > end if we decrement + SetColumnAddress( Device, 0x0, (Device->Height >> 3) - 1 ); + SetRowAddress ( Device, Device->Width - 1, 0 ); + + WaitReady( Device ); + + Update( Device ); + + return true; +} + +static const struct GDS_Device SSD1675 = { + .DrawBitmapCBR = DrawBitmapCBR, .ClearWindow = ClearWindow, + .DrawPixelFast = DrawPixelFast, + .Update = Update, .Init = Init, +}; + +struct GDS_Device* SSD1675_Detect(char *Driver, struct GDS_Device* Device) { + if (!strcasestr(Driver, "SSD1675")) return NULL; + + if (!Device) Device = calloc(1, sizeof(struct GDS_Device)); + *Device = SSD1675; + + Device->Depth = 1; + Device->Alloc = GDS_ALLOC_NONE; + + char *p; + struct PrivateSpace* Private = (struct PrivateSpace*) Device->Private; + Private->ReadyPin = -1; + if ((p = strcasestr(Driver, "ready")) != NULL) Private->ReadyPin = atoi(strchr(p, '=') + 1); + + ESP_LOGI(TAG, "SSD1675 driver with ready GPIO %d", Private->ReadyPin); + + return Device; +} \ No newline at end of file diff --git a/components/display/core/gds.c b/components/display/core/gds.c index 72567ba4..499a931b 100644 --- a/components/display/core/gds.c +++ b/components/display/core/gds.c @@ -133,11 +133,18 @@ bool GDS_Reset( struct GDS_Device* Device ) { } bool GDS_Init( struct GDS_Device* Device ) { - Device->FramebufferSize = ( Device->Width * Device->Height ) / (8 / Device->Depth); - if ((Device->Alloc && GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); - else Device->Framebuffer = calloc( 1, Device->FramebufferSize ); - NullCheck( Device->Framebuffer, return false ); + Device->FramebufferSize = (Device->Width * Device->Height) / (8 / Device->Depth); + + // allocate FB unless explicitely asked not to + if (!(Device->Alloc & GDS_ALLOC_NONE)) { + if ((Device->Alloc & GDS_ALLOC_IRAM) || ((Device->Alloc & GDS_ALLOC_IRAM_SPI) && Device->IF == GDS_IF_SPI)) { + heap_caps_calloc( 1, Device->FramebufferSize, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA ); + } else { + Device->Framebuffer = calloc( 1, Device->FramebufferSize ); + } + NullCheck( Device->Framebuffer, return false ); + } bool Res = Device->Init( Device ); if (!Res) free(Device->Framebuffer); diff --git a/components/display/core/gds.h b/components/display/core/gds.h index d0ec7ef9..49cd7c6c 100644 --- a/components/display/core/gds.h +++ b/components/display/core/gds.h @@ -13,13 +13,13 @@ monochrome mode is not such type of screen, SH1106 and SSD1306 are */ -enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1 = 1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7, +enum { GDS_COLOR_L0 = 0, GDS_COLOR_L1, GDS_COLOR_L2, GDS_COLOR_L3, GDS_COLOR_L4, GDS_COLOR_L5, GDS_COLOR_L6, GDS_COLOR_L7, GDS_COLOR_L8, GDS_COLOR_L9, GDS_COLOR_L10, GDS_COLOR_L11, GDS_COLOR_L12, GDS_COLOR_L13, GDS_COLOR_L14, GDS_COLOR_L15, GDS_COLOR_MAX }; -#define GDS_COLOR_BLACK GDS_COLOR_L0 -#define GDS_COLOR_WHITE (GDS_COLOR_MAX - 1) +#define GDS_COLOR_BLACK (0) +#define GDS_COLOR_WHITE (-1) #define GDS_COLOR_XOR (GDS_COLOR_MAX + 1) struct GDS_Device; diff --git a/components/display/core/gds_private.h b/components/display/core/gds_private.h index 74fd8630..6d3f46f1 100644 --- a/components/display/core/gds_private.h +++ b/components/display/core/gds_private.h @@ -7,6 +7,7 @@ #include "gds.h" #include "gds_err.h" +#define GDS_ALLOC_NONE 0x80 #define GDS_ALLOC_IRAM 0x01 #define GDS_ALLOC_IRAM_SPI 0x02 @@ -158,7 +159,7 @@ inline void IRAM_ATTR GDS_DrawPixel1Fast( struct GDS_Device* Device, int X, int if ( Color == GDS_COLOR_XOR ) { *FBOffset ^= BIT( YBit ); } else { - *FBOffset = ( Color >= GDS_COLOR_WHITE / 2 ) ? *FBOffset | BIT( YBit ) : *FBOffset & ~BIT( YBit ); + *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit ); } } @@ -166,7 +167,7 @@ inline void IRAM_ATTR GDS_DrawPixel4Fast( struct GDS_Device* Device, int X, int uint8_t* FBOffset; FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1)); - *FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | (Color << 4) : ((*FBOffset & 0xf0) | Color); + *FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f)); } inline void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) { diff --git a/components/display/display.c b/components/display/display.c index 8741cd7b..0cdbe25c 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -59,8 +59,8 @@ static EXT_RAM_ATTR struct { static void displayer_task(void *args); struct GDS_Device *display; -extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect; -GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, NULL }; +extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect; +GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, NULL }; /**************************************************************************************** * @@ -86,15 +86,18 @@ void display_init(char *welcome) { // so far so good if (display && width > 0 && height > 0) { + int RST_pin = -1; + if ((p = strcasestr(config, "reset")) != NULL) RST_pin = atoi(strchr(p, '=') + 1); + // Detect driver interface if (strstr(config, "I2C") && i2c_system_port != -1) { int address = 0x3C; if ((p = strcasestr(config, "address")) != NULL) address = atoi(strchr(p, '=') + 1); - + init = true; GDS_I2CInit( i2c_system_port, -1, -1, i2c_system_speed ) ; - GDS_I2CAttachDevice( display, width, height, address, -1 ); + GDS_I2CAttachDevice( display, width, height, address, RST_pin ); ESP_LOGI(TAG, "Display is I2C on port %u", address); } else if (strstr(config, "SPI") && spi_system_host != -1) { @@ -105,7 +108,7 @@ void display_init(char *welcome) { init = true; GDS_SPIInit( spi_system_host, spi_system_dc_gpio ); - GDS_SPIAttachDevice( display, width, height, CS_pin, -1, speed ); + GDS_SPIAttachDevice( display, width, height, CS_pin, RST_pin, speed ); ESP_LOGI(TAG, "Display is SPI host %u with cs:%d", spi_system_host, CS_pin); } else { From 0bf2c11fdb1a1edcb3ac4c0a96cf9768f7123d0c Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 1 Mar 2020 00:25:09 -0800 Subject: [PATCH 08/20] Correcting annoying AirPlay bug! - release --- components/display/display.c | 2 +- components/raop/raop.c | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/components/display/display.c b/components/display/display.c index 0cdbe25c..ddc79a0a 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -192,7 +192,7 @@ static void displayer_task(void *args) { // handler elapsed track time if (displayer.timer && displayer.state == DISPLAYER_ACTIVE) { - char counter[12]; + char counter[16]; TickType_t tick = xTaskGetTickCount(); uint32_t elapsed = (tick - displayer.tick) * portTICK_PERIOD_MS; diff --git a/components/raop/raop.c b/components/raop/raop.c index 07eab618..4d824f7e 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -631,11 +631,9 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) current = ((current - start) / 44100) * 1000; if (stop) stop = ((stop - start) / 44100) * 1000; else stop = -1; - LOG_INFO("[%p]: SET PARAMETER progress %u/%u %s", ctx, current, stop, p); - success = ctx->cmd_cb(RAOP_PROGRESS, current, stop); - } - - if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && !strcasecmp(p, "application/x-dmap-tagged")) { + LOG_INFO("[%p]: SET PARAMETER progress %d/%u %s", ctx, current, stop, p); + success = ctx->cmd_cb(RAOP_PROGRESS, max(current, 0), stop); + } else if (body && ((p = kd_lookup(headers, "Content-Type")) != NULL) && !strcasecmp(p, "application/x-dmap-tagged")) { struct metadata_s metadata; dmap_settings settings = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, on_dmap_string, NULL, @@ -651,6 +649,10 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) success = ctx->cmd_cb(RAOP_METADATA, metadata.artist, metadata.album, metadata.title); free_metadata(&metadata); } + } else { + char *dump = kd_dump(headers); + LOG_INFO("Unhandled SET PARAMETER\n%s", dump); + free(dump); } } From 1ed815466a4575cfaf7e9269e665017e15d74048 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 1 Mar 2020 10:36:15 -0800 Subject: [PATCH 09/20] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bb701d3b..531128de 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ SPI,width=,height=,cs=[,speed=][,HFlip][,VFlip][dri - VFlip and HFlip are optional can be used to change display orientation - Default speed is 8000000 (8MHz) but SPI can work up to 26MHz or even 40MHz -Currently 128x32/64 I2C display like [this](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi) and [this](https://www.waveshare.com/wiki/1.3inch_OLED_HAT) are supported +Currently 128x32/64 I2C and SPI display like [this](https://www.buydisplay.com/i2c-blue-0-91-inch-oled-display-module-128x32-arduino-raspberry-pi) and [this](https://www.waveshare.com/wiki/1.3inch_OLED_HAT) are supported The NVS parameter "metadata_config" sets how metadata is displayed for AirPlay and Bluetooth. Syntax is ``` From b2d90d36c173465f322c1d2fc1e9ebfd88834473 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sun, 1 Mar 2020 15:49:52 -0800 Subject: [PATCH 10/20] Ancient AirPlay issue fixed - release --- components/raop/raop.c | 25 ++++++++++++++++--------- components/raop/rtp.c | 41 +++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/components/raop/raop.c b/components/raop/raop.c index 4d824f7e..ffce9b46 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -57,9 +57,9 @@ typedef struct raop_ctx_s { struct in_addr peer; // IP of the iDevice (airplay sender) bool running; #ifdef WIN32 - pthread_t thread, search_thread; + pthread_t thread; #else - TaskHandle_t thread, search_thread, joiner; + TaskHandle_t thread, joiner; StaticTask_t *xTaskBuffer; StackType_t xStack[RTSP_STACK_SIZE] __attribute__ ((aligned (4))); #endif @@ -81,10 +81,11 @@ typedef struct raop_ctx_s { char DACPid[32], id[32]; struct in_addr host; u16_t port; + bool running; #ifdef WIN32 struct mDNShandle_s *handle; + pthread_t thread; #else - bool running; TaskHandle_t thread, joiner; StaticTask_t *xTaskBuffer; StackType_t xStack[SEARCH_STACK_SIZE] __attribute__ ((aligned (4)));; @@ -218,7 +219,7 @@ void raop_delete(struct raop_ctx_s *ctx) { struct sockaddr addr; socklen_t nlen = sizeof(struct sockaddr); #endif - + if (!ctx) return; #ifdef WIN32 @@ -240,7 +241,7 @@ void raop_delete(struct raop_ctx_s *ctx) { // terminate search, but do not reclaim memory of pthread if never launched if (ctx->active_remote.handle) { close_mDNS(ctx->active_remote.handle); - pthread_join(ctx->search_thread, NULL); + pthread_join(ctx->active_remote.thread, NULL); } // stop broadcasting devices @@ -515,7 +516,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) #ifdef WIN32 ctx->active_remote.handle = init_mDNS(false, ctx->host); - pthread_create(&ctx->search_thread, NULL, &search_remote, ctx); + pthread_create(&ctx->active_remote.thread, NULL, &search_remote, ctx); #else ctx->active_remote.running = true; ctx->active_remote.destroy_mutex = xSemaphoreCreateMutex(); @@ -592,7 +593,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) // need to make sure no search is on-going and reclaim pthread memory #ifdef WIN32 if (ctx->active_remote.handle) close_mDNS(ctx->active_remote.handle); - pthread_join(ctx->search_thread, NULL); + pthread_join(ctx->active_remote.thread, NULL); #else ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); ctx->active_remote.running = false; @@ -640,7 +641,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) NULL }; - LOG_INFO("[%p]: received metadata"); + LOG_INFO("[%p]: received metadata", ctx); settings.ctx = &metadata; memset(&metadata, 0, sizeof(struct metadata_s)); if (!dmap_parse(&settings, body, len)) { @@ -686,9 +687,13 @@ void abort_rtsp(raop_ctx_t *ctx) { rtp_end(ctx->rtp); ctx->rtp = NULL; LOG_INFO("[%p]: RTP thread aborted", ctx); - } + } if (ctx->active_remote.running) { +#ifdef WIN32 + pthread_join(ctx->active_remote.thread, NULL); + close_mDNS(ctx->active_remote.handle); +#else // need to make sure no search is on-going and reclaim task memory ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); ctx->active_remote.running = false; @@ -698,8 +703,10 @@ void abort_rtsp(raop_ctx_t *ctx) { vSemaphoreDelete(ctx->active_remote.thread); heap_caps_free(ctx->active_remote.xTaskBuffer); +#endif memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); + LOG_INFO("[%p]: Remote search thread aborted", ctx); } diff --git a/components/raop/rtp.c b/components/raop/rtp.c index beccdda9..0a3b6b3f 100644 --- a/components/raop/rtp.c +++ b/components/raop/rtp.c @@ -443,25 +443,23 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi LOG_SDEBUG("packet expected seqno:%hu rtptime:%u (W:%hu R:%hu)", seqno, rtptime, ctx->ab_write, ctx->ab_read); } else if (seq_order(ctx->ab_write, seqno)) { + seq_t i; + u32_t now; + // newer than expected if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_write - 1)) { // only get rtp latency-1 frames back (last one is seqno) LOG_WARN("[%p] too many missing frames %hu seq: %hu, (W:%hu R:%hu)", ctx, seqno - ctx->ab_write - 1, seqno, ctx->ab_write, ctx->ab_read); ctx->ab_write = seqno - ctx->latency / ctx->frame_size; } - if (ctx->latency && seq_order(ctx->latency / ctx->frame_size, seqno - ctx->ab_read)) { - // if ab_read is lagging more than http latency, advance it - LOG_WARN("[%p] on hold for too long %hu (W:%hu R:%hu)", ctx, seqno - ctx->ab_read + 1, ctx->ab_write, ctx->ab_read); - ctx->ab_read = seqno - ctx->latency / ctx->frame_size + 1; - } - if (rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1)) { - seq_t i; - u32_t now = gettime_ms(); - for (i = ctx->ab_write + 1; seq_order(i, seqno); i++) { - ctx->audio_buffer[BUFIDX(i)].rtptime = rtptime - (seqno-i)*ctx->frame_size; - ctx->audio_buffer[BUFIDX(i)].last_resend = now; - } + + // need to request re-send and adjust timing of gaps + rtp_request_resend(ctx, ctx->ab_write + 1, seqno-1); + for (now = gettime_ms(), i = ctx->ab_write + 1; seq_order(i, seqno); i++) { + ctx->audio_buffer[BUFIDX(i)].rtptime = rtptime - (seqno-i)*ctx->frame_size; + ctx->audio_buffer[BUFIDX(i)].last_resend = now; } + LOG_DEBUG("[%p]: packet newer seqno:%hu rtptime:%u (W:%hu R:%hu)", ctx, seqno, rtptime, ctx->ab_write, ctx->ab_read); abuf = ctx->audio_buffer + BUFIDX(seqno); ctx->ab_write = seqno; @@ -519,14 +517,21 @@ static void buffer_push_packet(rtp_t *ctx) { LOG_DEBUG("[%p]: discarded frame now:%u missed by:%d (W:%hu R:%hu)", ctx, now, now - playtime, ctx->ab_write, ctx->ab_read); ctx->discarded++; curframe->ready = 0; + } else if (playtime - now <= hold) { + if (curframe->ready) { + ctx->data_cb((const u8_t*) curframe->data, curframe->len, playtime); + curframe->ready = 0; + } else { + LOG_DEBUG("[%p]: created zero frame (W:%hu R:%hu)", ctx, ctx->ab_write, ctx->ab_read); + ctx->data_cb(silence_frame, ctx->frame_size * 4, playtime); + ctx->silent_frames++; + } } else if (curframe->ready) { ctx->data_cb((const u8_t*) curframe->data, curframe->len, playtime); curframe->ready = 0; - } else if (playtime - now <= hold) { - LOG_DEBUG("[%p]: created zero frame (W:%hu R:%hu)", ctx, ctx->ab_write, ctx->ab_read); - ctx->data_cb(silence_frame, ctx->frame_size * 4, playtime); - ctx->silent_frames++; - } else break; + } else { + break; + } ctx->ab_read++; ctx->out_frames++; @@ -637,7 +642,7 @@ static void *rtp_thread_func(void *arg) { u32_t rtp_now = ntohl(*(u32_t*)(pktp+16)); u16_t flags = ntohs(*(u16_t*)(pktp+2)); u32_t remote_gap = NTP2MS(remote - ctx->timing.remote); - + // something is wrong and if we are supposed to be NTP synced, better ask for re-sync if (remote_gap > 10000) { if (ctx->synchro.status & NTP_SYNC) rtp_request_timing(ctx); From 1bb4d25bcecdbdbeafa569ceadf14943520e91ae Mon Sep 17 00:00:00 2001 From: philippe44 Date: Mon, 2 Mar 2020 20:09:11 -0800 Subject: [PATCH 11/20] plugin screen width > 255 --- plugin/SqueezeESP32.zip | Bin 5479 -> 5479 bytes plugin/SqueezeESP32/Player.pm | 4 ++-- plugin/SqueezeESP32/install.xml | 2 +- plugin/repo.xml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugin/SqueezeESP32.zip b/plugin/SqueezeESP32.zip index f9688f9a4295a4ff6de82dcb47d085577f43272e..44b663b5c02128b712fb291afa2f2634d1d98089 100644 GIT binary patch delta 757 zcmVkb?$E+nl#gcNm+#cFkJ8ZV9cOiH7G^zlG? zZg=Q!>RO7D`_QSkL!g9y83v?R$t943o(bMg&RNJuHRqzDObW%&hE0J4*>@zzTY}~a zY5usqe++mk*z-2aUbBql7fX|x2r_@=0!XJ0<{9P;6m&WpXZXZFN#zZ__XoeQtim)iy1Ow4!w5 zDP@%q6bT89XkkwXAvd{6EgUTp zf3(op7=m|Oxai#;Zo#dnQC4V@xc8TPV@7r~{mpeu{0=Wv3qW{UThy;YBQva^Q#A_s z3-L)7cE{u$>(Bv?O*jJ9%zS^or({Ow=p5b)p*CRG2)#ECRzZyomk8X(25N3x0yez1 zYe<-^$&*^KmDrM&#?+C9+7hkhvNfNy*N-O7StU41>&=-pZ|r;w*UYk?)SPGGG4aI2 zfC0p7N)OUKZW4cpui)ZPml9+GVMLR2xWf&U%Wof{QeEe+nf-1M8l`_vEkqOEK;oHi zJtU=G5Y?zla>+qYo(iVlXFo{P*9Y_hW?2T8mk`eH;BA%>A)zM0_;eBErwgzD^$E4+ zH72vGQ01K*;X_Y~=wdr}FZVO13oaX#oQ3}GU&`~1B%{$YqTJI+)_LZg>+2y}J`~IQ zJb&T2lINY>Mfe0RRAYlc5tT2Gks&S`hKG(G!*dLbYR) delta 757 zcmV8V5WJeN{x(6o#Mw8+PEq`UQh$H8@uDCQB*YP*HctcN5 z09%i`gIrKi1QovXMtbJCM~a%iNvweUbq5X=7n0T=LW;V^Vzs(9jhDuJCZ$n8`gkBc zw>$JVbuC57edyHNAy7iU3^qX#a-~J?jiveczd%N(I~n<{9P;6m&WpXZXZFN%LZ<{a>eulr|)K&$gW+LsL zV2w0wnKWtYP+6a*X>#Bo)(ob$X_i&_-*+}n((DHh*uMMj`|dk?VAT)SA{2jOY zhUg>b!Y*{Ashi~rayn)Q!=G-gu$5yBcaBI_XylX8fva>0yexh zYlvB2lP9@iE3qXlg^4BgwIv$E`&NHYPCpE+vr2I6jWeg$yfOI{u9;!qX*e&!Q{st< z5j}|4lpcF~xpDL^x`K;C9SsBg7Y65WhZ`uDU*1C{yUsS5{%jB$sZM_lgaL0LcD%O^ zlE5?72a_s^#Rs)Nog@oLIvLTY~kf+3#b4236LFS_6wCW8 z`^RO)FXy@o_YbA4loSraidt6+&8T{qI|TiI1GCl&VF(EBx>8V5WJeN{KNB$q!6V0c n_2!dy6CMpabWKoqvO9as0RRAWlc5tT2Cqoqs=eZ~(G!*dgqUF< diff --git a/plugin/SqueezeESP32/Player.pm b/plugin/SqueezeESP32/Player.pm index fe6b208c..a1dc3e33 100644 --- a/plugin/SqueezeESP32/Player.pm +++ b/plugin/SqueezeESP32/Player.pm @@ -20,10 +20,10 @@ sub playerSettingsFrame { my $value; my $id = unpack('C', $$data_ref); - + # New SETD command 0xfe for display width if ($id == 0xfe) { - $value = (unpack('CC', $$data_ref))[1]; + $value = (unpack('Cn', $$data_ref))[1]; if ($value > 100 && $value < 400) { $client->display->widthOverride(1, $value); $client->update; diff --git a/plugin/SqueezeESP32/install.xml b/plugin/SqueezeESP32/install.xml index c8f6edc7..405670f3 100644 --- a/plugin/SqueezeESP32/install.xml +++ b/plugin/SqueezeESP32/install.xml @@ -10,6 +10,6 @@ PLUGIN_SQUEEZEESP32 PLUGIN_SQUEEZEESP32_DESC Plugins::SqueezeESP32::Plugin - 0.12 + 0.20 Philippe diff --git a/plugin/repo.xml b/plugin/repo.xml index 51d66d5c..838bd4d0 100644 --- a/plugin/repo.xml +++ b/plugin/repo.xml @@ -1,10 +1,10 @@ - + https://github.com/sle118/squeezelite-esp32 Philippe - 3e60650efdff28cd0da9a7e9d0ddccf3a68d350d + c17cce48b8fa14f995ca93f51fdab52b4bdcb422 philippe_44@outlook.com SqueezeESP32 additional player id (100) http://github.com/sle118/squeezelite-esp32/raw/master/plugin/SqueezeESP32.zip From 2b924fa425632817ef934e06ecfde23f25ea1eba Mon Sep 17 00:00:00 2001 From: philippe44 Date: Tue, 3 Mar 2020 20:37:23 -0800 Subject: [PATCH 12/20] Validated 256 pixels SSD1326 --- components/display/SSD132x.c | 33 +++++++++++---- components/display/core/gds.c | 4 +- components/display/core/gds_draw.c | 4 +- components/raop/raop.c | 65 ++++++------------------------ components/squeezelite/display.c | 5 ++- 5 files changed, 45 insertions(+), 66 deletions(-) diff --git a/components/display/SSD132x.c b/components/display/SSD132x.c index 6088d187..f7ed2b3b 100644 --- a/components/display/SSD132x.c +++ b/components/display/SSD132x.c @@ -162,15 +162,15 @@ static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, i *FBOffset ^= BIT( 7 - XBit ); } else { // we might be able to save the 7-Xbit using BitRemap (A0 bit 2) - *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( 7 - XBit ) : *FBOffset | BIT( 7 - XBit ); + *FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( XBit ) : *FBOffset | BIT( XBit ); } } static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color ) { uint8_t _Color = Color == GDS_COLOR_BLACK ? 0: 0xff; - uint8_t Width = Device->Width >> 3; + int Width = Device->Width >> 3; uint8_t *optr = Device->Framebuffer; - + for (int r = y1; r <= y2; r++) { int c = x1; // for a row that is not on a boundary, not column opt can be done, so handle all columns on that line @@ -184,13 +184,28 @@ static void ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int } static void DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) { - uint8_t *optr = Device->Framebuffer; - if (!Height) Height = Device->Height; if (!Width) Width = Device->Width; + int DWidth = Device->Width >> 3; - // just do bitreverse and if BitRemap works, there will be even nothing to do - for (int i = Height * Width >> 3; --i >= 0;) *optr++ = BitReverseTable256[*Data++]; + // Two consecutive bits of source data are split over two different bytes of framebuffer + for (int c = 0; c < Width; c++) { + uint8_t shift = c & 0x07, bit = ~(1 << shift); + uint8_t *optr = Device->Framebuffer + (c >> 3); + + // we need to linearize code to let compiler better optimize + for (int r = Height >> 3; --r >= 0;) { + uint8_t Byte = BitReverseTable256[*Data++]; + *optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1; + *optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1; + *optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1; + *optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1; + *optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1; + *optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1; + *optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; Byte >>= 1; + *optr = (*optr & bit) | ((Byte & 0x01) << shift); optr += DWidth; + } + } } static void SetHFlip( struct GDS_Device* Device, bool On ) { @@ -294,6 +309,7 @@ static const struct GDS_Device SSD132x = { struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) { uint8_t Model; + int Depth; if (strcasestr(Driver, "SSD1326")) Model = SSD1326; else if (strcasestr(Driver, "SSD1327")) Model = SSD1327; @@ -304,7 +320,8 @@ struct GDS_Device* SSD132x_Detect(char *Driver, struct GDS_Device* Device) { *Device = SSD132x; ((struct PrivateSpace*) Device->Private)->Model = Model; - sscanf(Driver, "%*[^:]:%c", &Device->Depth); + sscanf(Driver, "%*[^:]:%u", &Depth); + Device->Depth = Depth; if (Model == SSD1326 && Device->Depth == 1) { Device->Update = Update1; diff --git a/components/display/core/gds.c b/components/display/core/gds.c index 499a931b..d315b027 100644 --- a/components/display/core/gds.c +++ b/components/display/core/gds.c @@ -95,7 +95,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, memset( Device->Framebuffer, Color | (Color << 4), Device->FramebufferSize ); } else { uint8_t _Color = Color | (Color << 4); - uint8_t Width = Device->Width; + int Width = Device->Width; uint8_t *optr = Device->Framebuffer; // try to do byte processing as much as possible for (int r = y1; r <= y2; r++) { @@ -113,7 +113,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, } } } - + // make sure diplay will do update Device->Dirty = true; } diff --git a/components/display/core/gds_draw.c b/components/display/core/gds_draw.c index 7e6995e0..c85d39e6 100644 --- a/components/display/core/gds_draw.c +++ b/components/display/core/gds_draw.c @@ -195,11 +195,11 @@ void GDS_DrawBox( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color ) { if (!Height) Height = Device->Height; if (!Width) Width = Device->Width; - Height >>= 3; if (Device->DrawBitmapCBR) { Device->DrawBitmapCBR( Device, Data, Width, Height, Color ); } else if (Device->Depth == 1) { + Height >>= 3; // need to do row/col swap and bit-reverse for (int r = 0; r < Height; r++) { uint8_t *optr = Device->Framebuffer + r*Device->Width, *iptr = Data + r; @@ -211,6 +211,7 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int } else if (Device->Depth == 4) { uint8_t *optr = Device->Framebuffer; int LineLen = Device->Width >> 1; + Height >>= 3; for (int i = Width * Height, r = 0, c = 0; --i >= 0;) { uint8_t Byte = BitReverseTable256[*Data++]; // we need to linearize code to let compiler better optimize @@ -237,6 +238,7 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); } } } else { + Height >>= 3; // don't know bitdepth, use brute-force solution for (int i = Width * Height, r = 0, c = 0; --i >= 0;) { uint8_t Byte = *Data++; diff --git a/components/raop/raop.c b/components/raop/raop.c index ffce9b46..ceed0aa0 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -44,7 +44,7 @@ #include "log_util.h" #define RTSP_STACK_SIZE (8*1024) -#define SEARCH_STACK_SIZE (2*1048) +#define SEARCH_STACK_SIZE (3*1048) typedef struct raop_ctx_s { #ifdef WIN32 @@ -86,7 +86,7 @@ typedef struct raop_ctx_s { struct mDNShandle_s *handle; pthread_t thread; #else - TaskHandle_t thread, joiner; + TaskHandle_t thread; StaticTask_t *xTaskBuffer; StackType_t xStack[SEARCH_STACK_SIZE] __attribute__ ((aligned (4)));; SemaphoreHandle_t destroy_mutex; @@ -100,7 +100,7 @@ extern log_level raop_loglevel; static log_level *loglevel = &raop_loglevel; static void* rtsp_thread(void *arg); -static void abort_rtsp(raop_ctx_t *ctx); +static void cleanup_rtsp(raop_ctx_t *ctx, bool abort); static bool handle_rtsp(raop_ctx_t *ctx, int sock); static char* rsa_apply(unsigned char *input, int inlen, int *outlen, int mode); @@ -248,18 +248,6 @@ void raop_delete(struct raop_ctx_s *ctx) { mdns_service_remove(ctx->svr, ctx->svc); mdnsd_stop(ctx->svr); #else - // first stop the search task if any - if (ctx->active_remote.running) { - ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); - ctx->active_remote.running = false; - - vTaskResume(ctx->active_remote.thread); - ulTaskNotifyTake(pdFALSE, portMAX_DELAY); - vTaskDelete(ctx->active_remote.thread); - - heap_caps_free(ctx->active_remote.xTaskBuffer); - } - // then the RTSP task ctx->joiner = xTaskGetCurrentTaskHandle(); ctx->running = false; @@ -268,10 +256,11 @@ void raop_delete(struct raop_ctx_s *ctx) { vTaskDelete(ctx->thread); heap_caps_free(ctx->xTaskBuffer); - rtp_end(ctx->rtp); - shutdown(ctx->sock, SHUT_RDWR); closesocket(ctx->sock); + + // cleanup all session-created items + cleanup_rtsp(ctx, true); mdns_service_remove("_raop", "_tcp"); #endif @@ -406,7 +395,7 @@ static void *rtsp_thread(void *arg) { if (n > 0) res = handle_rtsp(ctx, sock); if (n < 0 || !res || ctx->abort) { - abort_rtsp(ctx); + cleanup_rtsp(ctx, true); closesocket(sock); LOG_INFO("RTSP close %u", sock); sock = -1; @@ -581,37 +570,11 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) if ((p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime); // only send FLUSH if useful (discards frames above buffer head and top) - if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime)) - success = ctx->cmd_cb(RAOP_FLUSH); + if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime)) success = ctx->cmd_cb(RAOP_FLUSH); } else if (!strcmp(method, "TEARDOWN")) { - rtp_end(ctx->rtp); - - ctx->rtp = NULL; - - // need to make sure no search is on-going and reclaim pthread memory -#ifdef WIN32 - if (ctx->active_remote.handle) close_mDNS(ctx->active_remote.handle); - pthread_join(ctx->active_remote.thread, NULL); -#else - ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); - ctx->active_remote.running = false; - - xSemaphoreTake(ctx->active_remote.destroy_mutex, portMAX_DELAY); - vTaskDelete(ctx->active_remote.thread); - vSemaphoreDelete(ctx->active_remote.thread); - - heap_caps_free(ctx->active_remote.xTaskBuffer); - - LOG_INFO("[%p]: mDNS search task terminated", ctx); -#endif - - memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); - NFREE(ctx->rtsp.aeskey); - NFREE(ctx->rtsp.aesiv); - NFREE(ctx->rtsp.fmtp); - + cleanup_rtsp(ctx, false); success = ctx->cmd_cb(RAOP_STOP); } else if (!strcmp(method, "SET_PARAMETER")) { @@ -681,12 +644,12 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) } /*----------------------------------------------------------------------------*/ -void abort_rtsp(raop_ctx_t *ctx) { +void cleanup_rtsp(raop_ctx_t *ctx, bool abort) { // first stop RTP process if (ctx->rtp) { rtp_end(ctx->rtp); ctx->rtp = NULL; - LOG_INFO("[%p]: RTP thread aborted", ctx); + if (abort) LOG_INFO("[%p]: RTP thread aborted", ctx); } if (ctx->active_remote.running) { @@ -695,9 +658,7 @@ void abort_rtsp(raop_ctx_t *ctx) { close_mDNS(ctx->active_remote.handle); #else // need to make sure no search is on-going and reclaim task memory - ctx->active_remote.joiner = xTaskGetCurrentTaskHandle(); ctx->active_remote.running = false; - xSemaphoreTake(ctx->active_remote.destroy_mutex, portMAX_DELAY); vTaskDelete(ctx->active_remote.thread); vSemaphoreDelete(ctx->active_remote.thread); @@ -705,8 +666,6 @@ void abort_rtsp(raop_ctx_t *ctx) { heap_caps_free(ctx->active_remote.xTaskBuffer); #endif memset(&ctx->active_remote, 0, sizeof(ctx->active_remote)); - - LOG_INFO("[%p]: Remote search thread aborted", ctx); } @@ -776,7 +735,7 @@ static void* search_remote(void *args) { LOG_INFO("found remote %s %s:%hu", r->instance_name, inet_ntoa(ctx->active_remote.host), ctx->active_remote.port); } } - + mdns_query_results_free(results); } diff --git a/components/squeezelite/display.c b/components/squeezelite/display.c index 91030330..3923467f 100644 --- a/components/squeezelite/display.c +++ b/components/squeezelite/display.c @@ -151,7 +151,7 @@ static EXT_RAM_ATTR struct { #define ANIM_SCREEN_2 0x08 static u8_t ANIC_resp = ANIM_NONE; -static u8_t SETD_width; +static uint16_t SETD_width; #define SCROLL_STACK_SIZE (3*1024) #define LINELEN 40 @@ -316,8 +316,9 @@ static void send_server(void) { pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2 pkt_header.length = htonl(sizeof(pkt_header) + 2 - 8); + SETD_width = htons(SETD_width); send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); - send_packet(&SETD_width, 2); + send_packet((uint8_t*) &SETD_width, 2); SETD_width = 0; } From 93c24cbf5f862b6128751a494e39f423c6e51929 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Tue, 3 Mar 2020 22:53:14 -0800 Subject: [PATCH 13/20] plugin update --- plugin/SqueezeESP32.zip | Bin 5479 -> 5847 bytes plugin/SqueezeESP32/Graphics.pm | 192 +++++++++++++++++++------------- plugin/SqueezeESP32/Player.pm | 1 + plugin/SqueezeESP32/install.xml | 2 +- plugin/repo.xml | 2 +- 5 files changed, 118 insertions(+), 79 deletions(-) diff --git a/plugin/SqueezeESP32.zip b/plugin/SqueezeESP32.zip index 44b663b5c02128b712fb291afa2f2634d1d98089..a64028a7e4a374a5e647c97b283aa86b295bc31a 100644 GIT binary patch delta 2758 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 delta 2411 zcmZWrc{tPy7ycPpM-0Z;CPpd6SRz7%OJ*8th-@Rf7-OHY#E(&kCNWo1*|S7LMcJxJ zDP&2qRb(Aw8*-UTB%gc#_`Z9e?~n7G^PJ~A=a2V!-}9bfRhBYnYr(w_4gdf@;MQbk z8-SNY@9qPDd>8;g_qv#H&(MHN-Vth{!95q^BKnPCyRxU>>PHD(PfF_B3V3=baNvfq z^iBQK`j%Kk$L(BluH8Tad1KiQhYQN6^!7*fuooK`V}xZaaw9{_WR7>fEpv03xEqwd zxu2cT;K>G;U;0^Va@=1!q05m;(Ak!QEz)}HoG8ZyzJ$h>?S9tTiEc?@^VJ9Edrv%l zXS~V9GpC89F>*ML1`3=|VY2uGE*lekmgmetb?Ix63(PO4{#*!x4JKJeO}*$ zM6c>M)SnZ%Lr=~mg^cJejrg4Z+Je@Vsb`Z(nLZ##!kH1oh#r=$Pwla;<4l(JKXI)8 zb~1CxDfNYpfUNUG-paLOV~b15+v4!Urm8Z&o2%RlLx~=G&W|c<8rPjug7)F=BEisdazyp!_w;fNHj|Gyp|Lh8ico-s8 z6BeZ_e%%j)C+0@R+m|=2X~dFRV*2#vp1;&t!Hta`T{)0=ux~tkK$8AOlCe-e2p>a{ z7)K#Pg1-_Y*911$^BbK+?)!(cl7Xagp<aZRkC=*b`ieUP}kKj1r^4zRK?piIfOv^}AF3U=E~?e427XJR}Y zDw?W@(ipu0v_o{QK(t41oVK($8Z!FGlE;$L5Kb_7Ul+F1IC?ubh2!!4N%k$!2NMrB z%j1nV(onaF=#+_ZhixiGChEQ| zvL!y$8#F%*C9Bj<%(H*Q|eNeZxJ8un#Y`yluUgYhvQ!g(d^0PxLJ(s{zTec>F$-D-5oRM!v`X?itwe*%nr)R;YqWy( zXAE1ktxj2)nXfe_LkQ>FNETU+*8TI)I!;gw= z3^PLQLw?Ri2RivaL7lWE&HcHlO11hJ+i0xPJeA@d9!Dw|KNFi?Vrr$Q+`(dpQ-w!8 zbes;H;fLd6zE>UqhryLutth8k=eW3Ubk%^twLN8?CeIcS$VADmUYNbn7KBccPczL- z^BK^}l0+%_P49Eo;B$AX2@+}=W)peuYTy~{ zH5PwrY+8Y^(w(je@%*z>w&d6+oWPmhTCej)5Yz}#j^O_h0e-#l(GB+6<>?ocksQIP zTBK9u2YDK+8~NJNuq*$jT(WAoT0pd!U^Cq96<-yw;T?VX4(FbwPEGzs)pY4DqCHkpkw>4UUi+0)?B&3Trpr`B zx;P!-yeOpg^2*c|@`ggdr6Moei(6mJ#JB)pVK4r=n3sqVk)8yCT68cWILpGKO#^;) zgR)!PLdoz$Vy=WMl?I5K+!uRy^{&|0MXB!6BlEU9obuWSuhAd$JeChh>d>=4%F)Hm zYp2(IL@~V)?qQcs)J%s&uBH?e0MF#2_?n7J_?0iSsO#MkU?yA}q+Q_pu_%mGR#T)K zU4chY-v&kn%cxU-SmH_(*H?{WiR8JE1()>f?Y;*MrbSm0qMC{A~SYbw^m|X40MV@ysrCHWPxa5N6^sMrAG5R>7p} z)@02;R{R+Mvm1py@PgjsN4xicBXI+Oz#e$E1kV`X@IAAMvU3mlqydj7CcGWgQN31Q z5;=X$HwOk65TFKl+VCr!Y&xro1W(oF=TNrBn@FQ1iM#SY!&aJDn{F-sgX%*E;a&kv zCt~oYh10-LXT-_j7(D;8GlrQZtthGL7++Qb!|Q}0G28AtQREc7#akxb{8ciO(!r=C z{Q~RzMx-cY*T%UqIbtil@|5FoVPjY#7<9?~JKI`fHkVf}FCkzVJ+y12!jJnfLB%R^wQ zFT_x>3=bQuv2qGE$m$zf(TpVw_negmN;pFv3(>wvxIzSkX(th`kbDu^?+8O)yS+~e G0R98A>RHqP diff --git a/plugin/SqueezeESP32/Graphics.pm b/plugin/SqueezeESP32/Graphics.pm index 70909e7d..db58703d 100644 --- a/plugin/SqueezeESP32/Graphics.pm +++ b/plugin/SqueezeESP32/Graphics.pm @@ -15,84 +15,37 @@ my $VISUALIZER_VUMETER = 1; my $VISUALIZER_SPECTRUM_ANALYZER = 2; my $VISUALIZER_WAVEFORM = 3; -my $width = $prefs->get('width') || 128; -my $spectrum_scale = $prefs->get('spectrum_scale') || 50; +{ + #__PACKAGE__->mk_accessor('array', 'modes'); + __PACKAGE__->mk_accessor('rw', 'modes'); + __PACKAGE__->mk_accessor('rw', qw(vfdmodel)); +} -my @modes = ( - # mode 0 - { desc => ['BLANK'], - bar => 0, secs => 0, width => $width, - params => [$VISUALIZER_NONE] }, - # mode 1 - { desc => ['PROGRESS_BAR'], - bar => 1, secs => 0, width => $width, - params => [$VISUALIZER_NONE] }, - # mode 2 - { desc => ['ELAPSED'], - bar => 0, secs => 1, width => $width, - params => [$VISUALIZER_NONE] }, - # mode 3 - { desc => ['ELAPSED', 'AND', 'PROGRESS_BAR'], - bar => 1, secs => 1, width => $width, - params => [$VISUALIZER_NONE] }, - # mode 4 - { desc => ['REMAINING'], - bar => 0, secs => -1, width => $width, - params => [$VISUALIZER_NONE] }, - # mode 5 - { desc => ['CLOCK'], - bar => 0, secs => 0, width => $width, clock => 1, - params => [$VISUALIZER_NONE] }, - # mode 6 - { desc => ['SETUP_SHOWBUFFERFULLNESS'], - bar => 0, secs => 0, width => $width, fullness => 1, - params => [$VISUALIZER_NONE] }, - # mode 7 - { desc => ['VISUALIZER_VUMETER_SMALL'], - bar => 0, secs => 0, width => $width, _width => -20, - # extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), bars, left space) - params => [$VISUALIZER_VUMETER, 20, 32, -20, 0, 2] }, - # mode 8 - { desc => ['VISUALIZER_SPECTRUM_ANALYZER_SMALL'], - bar => 0, secs => 0, width => $width, _width => -32, - # extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), bars, left space) - params => [$VISUALIZER_SPECTRUM_ANALYZER, 32, 32, -32, 0, 2, 6, $spectrum_scale] }, - # mode 9 - { desc => ['VISUALIZER_VUMETER'], - bar => 0, secs => 0, width => $width, - params => [$VISUALIZER_VUMETER] }, - # mode 10 - { desc => ['VISUALIZER_SPECTRUM_ANALYZER'], - bar => 0, secs => 0, width => $width, - # extra parameters (bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, 16, $spectrum_scale] }, - # mode 11 - { desc => ['VISUALIZER_VUMETER', 'AND', 'ELAPSED'], - bar => 0, secs => 1, width => $width, - params => [$VISUALIZER_VUMETER] }, - # mode 12 - { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'ELAPSED'], - bar => 0, secs => 1, width => $width, - # extra parameters (bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, 16, $spectrum_scale] }, - # mode 13 - { desc => ['VISUALIZER_VUMETER', 'AND', 'REMAINING'], - bar => 0, secs => -1, width => $width, - params => [$VISUALIZER_VUMETER] }, - # mode 14 - { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'REMAINING'], - bar => 0, secs => -1, width => $width, - # extra parameters (bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, 16, $spectrum_scale] }, - -); +use Data::Dumper; +sub new { + my $class = shift; + my $client = shift; + + my $display = $class->SUPER::new($client); + + $display->init_accessor( + modes => $display->build_modes, + vfdmodel => 'graphic-x32', # doesn't matter much + ); + + return $display; +} + +=comment sub modes { return \@modes; } +=cut sub nmodes { - return $#modes; + # -1 for LMS bug workaround + return scalar(@{shift->modes}) - 1; } sub displayWidth { @@ -116,12 +69,6 @@ sub displayWidth { } } -# I don't think LMS renderer handles properly screens other than 32 pixels. It -# seems that all we get is a 32 pixel-tall data with anything else padded to 0 -# i.e. if we try 64 pixels height, bytes 0..3 and 4..7 will contains the same -# pattern than the 32 pixels version, where one would have expected bytes 4..7 -# to be empty - sub brightnessMap { return (65535, 10, 50, 100, 200); } @@ -132,12 +79,103 @@ sub bytesPerColumn { } =cut +# I don't think LMS renderer handles properly screens other than 32 pixels. It +# seems that all we get is a 32 pixel-tall data with anything else padded to 0 +# i.e. if we try 64 pixels height, bytes 0..3 and 4..7 will contains the same +# pattern than the 32 pixels version, where one would have expected bytes 4..7 +# to be empty sub displayHeight { return 32; } +=comment sub vfdmodel { return 'graphic-'.$width.'x32'; } +=cut + +sub build_modes { + my $client = shift->client; + print("CLIENT IN BUILD MODE $client\n"); + my $cprefs = $prefs->client($client); + + my $width = shift || $cprefs->get('width') || $prefs->get('width') || 128; + my $small_VU = shift || $cprefs->get('small_vu') || 0.15; + my $small_spectrum = shift || $cprefs->get('small_spectrum') || 0.25; + my $spectrum_bar = shift || $cprefs->get('spectrum_bar') || { 'small' => 0.1875, 'full' => 0.125 }; + my $spectrum_scale = shift || $cprefs->get('spectrum_scale') || $prefs->get('spectrum_scale') || 50; + + my @modes = ( + # mode 0 + { desc => ['BLANK'], + bar => 0, secs => 0, width => $width, + params => [$VISUALIZER_NONE] }, + # mode 1 + { desc => ['PROGRESS_BAR'], + bar => 1, secs => 0, width => $width, + params => [$VISUALIZER_NONE] }, + # mode 2 + { desc => ['ELAPSED'], + bar => 0, secs => 1, width => $width, + params => [$VISUALIZER_NONE] }, + # mode 3 + { desc => ['ELAPSED', 'AND', 'PROGRESS_BAR'], + bar => 1, secs => 1, width => $width, + params => [$VISUALIZER_NONE] }, + # mode 4 + { desc => ['REMAINING'], + bar => 0, secs => -1, width => $width, + params => [$VISUALIZER_NONE] }, + # mode 5 + { desc => ['CLOCK'], + bar => 0, secs => 0, width => $width, clock => 1, + params => [$VISUALIZER_NONE] }, + # mode 6 + { desc => ['SETUP_SHOWBUFFERFULLNESS'], + bar => 0, secs => 0, width => $width, fullness => 1, + params => [$VISUALIZER_NONE] }, + # mode 7 + { desc => ['VISUALIZER_VUMETER_SMALL'], + bar => 0, secs => 0, width => $width, _width => int -($small_VU*$width), + # extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space) + params => [$VISUALIZER_VUMETER, int ($small_VU* $width), 32, int -($small_VU*$width), 0, 2] }, + # mode 8 + { desc => ['VISUALIZER_SPECTRUM_ANALYZER_SMALL'], + bar => 0, secs => 0, width => $width, _width => int -($small_spectrum*$width), + # extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space, bars) + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($small_spectrum*$width), 32, int -($small_spectrum*$width), 0, 2, int ($small_spectrum*$width*$spectrum_bar->{small}), $spectrum_scale] }, + # mode 9 + { desc => ['VISUALIZER_VUMETER'], + bar => 0, secs => 0, width => $width, + params => [$VISUALIZER_VUMETER] }, + # mode 10 + { desc => ['VISUALIZER_SPECTRUM_ANALYZER'], + bar => 0, secs => 0, width => $width, + # extra parameters (bars) + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width * $spectrum_bar->{full}), $spectrum_scale] }, + # mode 11 + { desc => ['VISUALIZER_VUMETER', 'AND', 'ELAPSED'], + bar => 0, secs => 1, width => $width, + params => [$VISUALIZER_VUMETER] }, + # mode 12 + { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'ELAPSED'], + bar => 0, secs => 1, width => $width, + # extra parameters (bars) + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width * $spectrum_bar->{full}), $spectrum_scale] }, + # mode 13 + { desc => ['VISUALIZER_VUMETER', 'AND', 'REMAINING'], + bar => 0, secs => -1, width => $width, + params => [$VISUALIZER_VUMETER] }, + # mode 14 + { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'REMAINING'], + bar => 0, secs => -1, width => $width, + # extra parameters (bars) + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width * $spectrum_bar->{full}), $spectrum_scale] }, + # dummy for LMS bug workaround + { desc => [], bar => 0, secs => -1, width => $width,params => [] }, + ); + + return \@modes; +} 1; \ No newline at end of file diff --git a/plugin/SqueezeESP32/Player.pm b/plugin/SqueezeESP32/Player.pm index a1dc3e33..e694d03b 100644 --- a/plugin/SqueezeESP32/Player.pm +++ b/plugin/SqueezeESP32/Player.pm @@ -25,6 +25,7 @@ sub playerSettingsFrame { if ($id == 0xfe) { $value = (unpack('Cn', $$data_ref))[1]; if ($value > 100 && $value < 400) { + $client->display->modes($client->display->build_modes($value)); $client->display->widthOverride(1, $value); $client->update; } diff --git a/plugin/SqueezeESP32/install.xml b/plugin/SqueezeESP32/install.xml index 405670f3..812417df 100644 --- a/plugin/SqueezeESP32/install.xml +++ b/plugin/SqueezeESP32/install.xml @@ -10,6 +10,6 @@ PLUGIN_SQUEEZEESP32 PLUGIN_SQUEEZEESP32_DESC Plugins::SqueezeESP32::Plugin - 0.20 + 0.21 Philippe diff --git a/plugin/repo.xml b/plugin/repo.xml index 838bd4d0..ed988600 100644 --- a/plugin/repo.xml +++ b/plugin/repo.xml @@ -4,7 +4,7 @@ https://github.com/sle118/squeezelite-esp32 Philippe - c17cce48b8fa14f995ca93f51fdab52b4bdcb422 + f1955a3c930a0c1809a1d2dac85970905c9b68a0 philippe_44@outlook.com SqueezeESP32 additional player id (100) http://github.com/sle118/squeezelite-esp32/raw/master/plugin/SqueezeESP32.zip From 831410752a88869436f9e80b85adcb960cff8c38 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Tue, 3 Mar 2020 22:57:07 -0800 Subject: [PATCH 14/20] plugin update --- plugin/repo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/repo.xml b/plugin/repo.xml index ed988600..91822b34 100644 --- a/plugin/repo.xml +++ b/plugin/repo.xml @@ -1,7 +1,7 @@ - + https://github.com/sle118/squeezelite-esp32 Philippe f1955a3c930a0c1809a1d2dac85970905c9b68a0 From c865dfbe9c9f0ddf0b7bedf1de26e6997c713365 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Thu, 5 Mar 2020 19:46:06 -0800 Subject: [PATCH 15/20] By player screen settings --- plugin/SqueezeESP32.zip | Bin 5847 -> 7560 bytes plugin/SqueezeESP32/Graphics.pm | 49 ++++++------- .../plugins/SqueezeESP32/settings/player.html | 28 ++++++++ plugin/SqueezeESP32/Player.pm | 1 + plugin/SqueezeESP32/PlayerSettings.pm | 66 ++++++++++++++++++ plugin/SqueezeESP32/Plugin.pm | 14 ++-- plugin/SqueezeESP32/install.xml | 2 +- plugin/SqueezeESP32/strings.txt | 38 ++++++++-- plugin/repo.xml | 2 +- 9 files changed, 160 insertions(+), 40 deletions(-) create mode 100644 plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/player.html create mode 100644 plugin/SqueezeESP32/PlayerSettings.pm diff --git a/plugin/SqueezeESP32.zip b/plugin/SqueezeESP32.zip index a64028a7e4a374a5e647c97b283aa86b295bc31a..743e2fe5c00d4a77c925a6c043dd438cd3979188 100644 GIT binary patch delta 5455 zcmZXY1yod97srQ?W`KdAOS(b2l#q~85Tr#KhK@l(;UZGfFeBYvgMc6muUoQgtsY4@a1dw~&YHi19P;Nk#H2hC4m7J|udI%qX&QrW&;!0wmT1T0;9CxIY#D z%VSN7)7@PQW?dfFnmC)1DUOEr`5AY2sg3TswhVSWS1CrBw$kZl1?Ao#(SrFZLnokK zY9TX*NsH0`ZJs47hpzyg0*Uqo#wLBMInJn})vLQ(!l5ig$-`p8#y0eaMxQE<>u7kX zyG<)a4EgTJ@@^>kPsSgE(ilN5uQ?SNW2(fbTwK#p;b(+5Ze! zSj3oG$&#!qU1J5jQRrmcnSL)8JO~Uj)Vj?}2Q6(#oF5vtqBq_uKa$jC*AW*TmMK6wz4}cj zAfIhbgpD#UQ96vbC^9NEZm*wl_-4j{Hj`Q0*Ha24|AYM#0r@GPM2dQNt|8ZOlKg=O zr`Q<55S)&XpQAg+k60*A`Rj#tsBu-B&M|(E6O@|JkCx5wg?op1+7lDa-t0CCi>OnZ3iHY8cN|L;VW}P)y~<-+T7+>o9b;|S-4uqSOe7e zgSkCqdzWwiq|<&mUVn?&fn-*u-o;-12uWuUaUZO^rt{sUzVrQ~HbKfb!%614No~Sv znTn(jHA7Tvx#f8(grBD82=%OW)UjfVj8DBpQF)Xvl1V-Qw$`q!*KYw3A7|XP>Wkz2 z>_=VmGRw89`YX{#wR-NYQ*9H{bg7x2H2npbw7MG-M#oz`)$e`}ZH`rbZUW$+g=5@c zsQ31$MVY{5hT@jqbBmUx zz-MP@yS+o_0!;IaW1YyV98@ya-U-w4?7(j%Yh>mssKLZt{Ha$-PL=d5f~cuZeIcHl zY)IOfjeEA~I~R{{TK6tt(>?XM3{*$hf*6mLFfFj(fg>?_Apg@cz$%b@*(@;vJB8Hl z$?`Gd)C{DC&dfrlu}5LM@suAYcbT7N>eN;+YrZ}8@}c$0sEKqsKMjrFUE%X+AOm(* zzHEJaY5wH{QD6!b8MUZvxM1!aT0QViOYcCiPSLeO`RrcgJIFCe>nO*5d2XeScxmR- z-keT9-;Qpe+?l7zuM6Jwj8o=tDIH&XiKW$j9Y}WE+jhAJ!X>%2#5Woax34&gni+Vd z=#LxL?5@WAPHomN8JueA4j;Cj0twoZW_K<~5unH9ZB2+y&hx%}xcT$TiD;cTM zJCc+>B(_{a#7aB}+=TJkjassH;>al3?T-z_$91jieT0E6Dz*CyT;)-;s|#p_*+V2f zHED(ZZqtwbR&${jj)ir$X*_8Jo)IkuU0x5KZG+*5s_eGfk$f||2YL;F*z>Ob{9ys| zA58i0Jl_Ll9~(;z=;K&a7}2Vrc`w;@+Axv6VPBelIB74(aHFaqK7HHQ7d!-IC_sif z$NIZ5#3DH-oI#1kKJG?kb8@E@?X7i&W{HJLgLIFBAQS5}5?PJYCs$7G(_0&FOrvs* z-)h`c?}&d-NP26Z@kv($Af-#|P54#TAZALj%g0xegXdxK2UL@s_^99I-Z z21UK8*fESRWK}_yEGw$YV>|7O?8EKpg>=fkFYVM02v&0Cdqbjds=WYdzBI3wy3h0b ziR$ITAN<70Ri-6Mwm7AvW>hjJ@PKex))l^=myVG&G*oKpFop0 zkXd%RdvL-y$n^?uUZ+2>0p{inw{me2@^f`@P1Mrr7NH6|4!O!}35m9ctA%=T7dTQV z0fms>unfrj7VU5W*M{z|!{Rq(T>@h=Pj=a9BvDBd%qU8=H!B!h3iVO%$DT0psug!Q zIy^fY^p4pFzbQALfbaQw?7^^iJe351FvdFF0yFXnaiA1JseEF4oP1pAg=0^(H4B0d z(24|$IBY-=7t8*;T0MI1DpvjvkAZ4A2sP$msrow*Cy&257&8t``MSH z&n#q(2zJ@n3i$EOVkCzw-o~sB5wkuP-PzOR-`t(JRo6H)E0~a&uEnn}Axf1pa^Eqf zc)LRBqVrNG9W0sfBE5Jien=o`75iQZcvUlcfk6v{242M6_Z8jWvv2=%@_;{~3yHAP zH7QlTyL&xzTR0#P;dQC*UykA7>htii^SlUE;5(qFNU)w{$YbudOof#T9f=yv<{>O5 ze|9D=^+Hd>Ejjk$*kV9)a=e!;8NS!FzVIfS2JnlmPPi**npJ$d5`03oe){c0|Y`T+Ixno@L1)uB+3!vx@208%@EFuh#&pL^Di;UQjS;= zT#O+8s`kZ1u{>Wta8BX&Crd+uR(@t}d``oBRdTLLT_V7hshYaT1Z%}rL&8U^4aqS1 zkk;%WOGd%1eAJ=Q>cNK&L&JM7P@X*-uSW3ePBqcnVB`|> zxB7KWqWEWH=-a{JFgJ%knEbVY-h*$759nn#i~5*-b6EI);YD=9bsFU)z_qlMP8|Tkq&Li(y8NM@9kd&V2P`))>{apXT^V zCkAY7%OZ?VEO0xGt0l_GW54?6pZBTvsTZk@tD*Z3D&lVx26NmFPYow z)3iQ^+d1rE#gt+Wn{w-LF}Eu`I2PStk!I#MH{5tDk-6Cw)CHZD;u~iF*jA!+^zwjmI-YTR(!94E03tJW z)O@B7KD)7GZMZ6ALaTBD$_BchmF>Q}Go#qMwbL_q-xNJtm@CHBeKA7yB6=-S8q#J&e8`yCpt~jJ9>b8*l_ZNI#x_FDB*&#HHqtXjqJFK7 z_RYfyaswF4Ndv-?*MxI=YCzPYHiq+#LY=(3k#S+I_L9+ zt=Jz+#|!jp+&1Jk?82T&3h2k4bXH0D=;O>v4QB~wQ*cA!tfF)tg=N%F4F^dxkZ~(j zKNh&WWP=@z`Cqa8>sle#Cl)m>z&D(BExNFOMAydw=63BS^vxX_>>!SNZhNDr_u@j_qXZ~jOpd#+l=EK zhHxT&W|CR*d*&p_P}fdeGArm}sQr&UFpb(x!{u}Q`e?;ke;>^+2ArEBQhN$i|l|2lmF{uaDrk|{a3o0^gGJ zw%j91QdKAC_Y@(;bFbRK9D04z5r~zQJ3mKmbvHM>kyGs~luIqgi|*da*#w>?IcDn)wqEZ#q^Sr)sFyDX z$)9)}KfU7vtoarUw8u191-+3=OEAe>%F+6h*q2%vfANSeM<&Ly>%sUWq-|)D zl|w8Fl<$=l5;`FVt+6RHFIB)X8N&48Y%6DxGr^6&777_uiAvF*73rr0H7zO3zldiT z8A4=8twSSu&j}ccXC%_mCHIdLht0qF+nY)q(`|DALf+le6yL1Hf3{KgghLV;`tH3M zi?^vzqNXOahLx9Li-CEc_0pD#LW`B1^c||}Qx_Z6LmF4AR!+z;Y+A}Iw9IR{v&3ym zrPz<(gAu$oZYd?2ty%+y&cD#=#o}h7F;#J|9%w9OjP$k5@5vVSBdtsAl7oq0!zbm` zm`9Dk!vgKdL_wER^z%^A7GhlGV5FiAt-D?TM@P;ZhkB%^n_Nlci{iKSUVnaXYF)PFve1c1xu1LLSdL`tiN&u8I?G;6#n0l5278P2;oa5!usFf zh~s)I|M9S1r%r;uE2#VyDiQtp4+O#j(SpwSgcr@PTk%e2!TO`MzjIwJENlhPU$=Vy z;5nk23VO4h=>OpScVp_|(eW@0VvUOV_J5)D_kNvS``bT=`nv`8yGXPJ2B8FH2L4Mg zzgr*!|83znmigTh-?HZq$Lj(0(ezyXp8r0uzf}Bp5&yRdToA~X0s%m2@Qn2E1FlDv z36;m@phV0;Z!7#)J^rl+0zJQ;wcoVl_fB0(2y0%OGNhKro~1@0sU^TR|?fs4u%3o=A5GM_ZJ*yUU=NFKR@Y=_37Q)T$&I9LCuhBEnx|01cAj@lue} z(PZg?lW)7;uh_!};L_bgM_4urF5EYN&-%4ICl|=`dit888t=YzRBX8Rl9uvVCB_wJ zTx>iG7@(xQZ+Ms2vPu!f724Yw`{k#&%dVA2r_($R&FawT;{ck;dQSN)b&+$Rdq?$I4Sw5YL- z-a6j8#NfgPeNwWR9AtPLvecNaX(+vBj-Rgav;*x$Bj8ha4`D)hE# zRNRK|R)&U492(M|oO^HmRNcaU*|GmH`*5y$Sd4|o3S)@MTg&ndLZm)Un}+0#V&hXY z_p`;myOPbL-o)`^(o-vj)9IoJX0o8SI$%dCr5h7=d}q#^G{-9bD|BzmIE!p8u5*0` z-Wba5H#JJzOBwqm^>p$h0#!L8yq3T0Sy-&fTc&zjVsvnEM{r4j>DUD3^kaiWc=P(GWoK0j0Sd-dBK)8V zHb+i9qPv5+`=xt@Hi(e~LW}|rdV1I{8h3bhs>9F!g55DxNl%@ms43V}C_;H&%Wt?M zi~Tfu-oqXf84*{S5$Z2)dRI|qbJg>+zUX=_(cAPo+b4doQU1Vn0Z+5z#D+Sv5po?F z6wzku8XNXitDx+YyAxePKn5cZE2E8vji}!UO_6FMapS&#e~sLuS#8(;z??UOn;kd3 zDJ@=`iyi7GtLZY1dIyM<#ysO~s25Mhh)8Ecyr<6F)$tAip)aUs3G1=^4%}Cv6<=>% z+({lXA61VdG4(1W5pk`!ePdqjW}lN7zeG(2Yt(1{oPl!8q~QGTR^Y zp;E^#Jbd;SdvcwbUInK?gv1Fkrc^}B;bE9CS4x(#Q=K4b#p>sB$&bi71>t05W;GuR zbA2oqWz2#PV^&67RgR4Cy51teH|v2qw~{pxw?*)T=@ueq{3Ht-WfC`}St{pU+W9W- zS!|C+s6#f7CtrAz8sLw(Gl6Zhd}J)(c?&QpC8E0^-^=xpT(A*JpK*u~SnJucFb=jA<7~WZW4&_1am3#R6AcS2` z?CXQP1e)f0_eQ^lua)jrOl&`&y9&G*^*bH^(YPheE=F^#3xtU^m^e@2p^DnWQ)gPE zJ2FnOUApL#Xhw3u7cxz$adFIiITZ2Kx`=pV%I;25$LlR-(GR}gJ-NQ*0ikjH9^{|a z^FujgZTgs}mE4e4!cuBUmJbna$DlGv&`w!;P^Y2;xrJ?hQ~$HBj=Oy|lFjvE?NBfW zN;}-bYQz;U323W(<7%VX+@xk{2m*G{ZKp8>Ym0!X^<1?<19(M=+DQ3Dvaer4FvkZV zMF(%@8F>XWm+k7kapq_eZQkBhTQg0^-L@C7d} zFj~h#^llwigk6oUkj86XAg1>tWQc!A=RRRV-dCEXL1EP&brprg3qiNO$RP-bCfeSye8 zNTQzzedYl9&m_1JjS6u_stcn^mGin7mmUK zHoA)isn4b2qi{y&kZ0T^A?m~GyCVbm=Enp-GmZN1TaX%wn(j%~*g*Mq(ga{5?YQ3| zuY8=u?UwuajG|?Tl|l9m0}bSk_@QpxX2I!){`ey?YxEV+yrT=C-$Z$Zpsze*BJ}-g zsK&!d?!;Tg_o!vz^B^47_E21EKVOrqvplDTa zL~oRW7pZ7Rcr{a3^4EzK2bz@F-|sSB!YYRDe==aV*DdpLS+T9N3CA0uGDEvN_!)^` z(3J@(YRZ2TI%Bd1TBn-D1$s!%Sao#g$tL0@0RQkxCkwy9CB0bv=j)SqVe`^zuaBoy zY}QE)VjHfpZv9%SczlLBh*q;|(vWzZe9iqOWLMm zf@<|S3B_Im7V>EU@Rt7MTE~;Vj-34VDKgwda!L_nThw%y5IiIihN3vRApegM^y&@u z`F@)hR9_n5&cBV|W$);EX@oFC7pEB*-EZgfs2m#Skei7~pY$Cj%_|LrtbkO?Q#0e^ z%?OcXzFz8wJ-}S=&=2#I1={58?fTVB9EN#M1x}%jmoEC1kun3>r4{d6K2F;Iq-a~b zPjN@~dV6mjD`954((uEe66b2l(xAQKV~XP7k<0~%(?#$_7rdi(s-2vJ-W|NF_Nqkf zZEj8e`44i$kF8_)pfFN74p=EN;gttzdlykT)up!Eoz(NpSR{S&N|%=i>tZ{Ci%&`_ zF^}eD8-9Khv+}O!ME%)ua+gHc9#^&>bMW($G zVU8rnQO&(-v=+*Rqv7axVs4y##Q8x(kmMX*)!8RiI=*f)>T6qfJ#hT7$nh4Xw(%9H zvzldhS?BUP`(6Zs(^Y=i8jp_TRb##x+)NA|wD?B9-D5T~A*fexF88`{4PEuBNXxaoZw^cEqM&%x+yEVap8Av;hMN4Tk!T z1L${7msfjE^sj_Op*MmOr9*Q+!42jqU(|t&6Rh9{UQN>vk z0{7l>{;cft0*83lmDPdsTD|Z%*)S&H+Jho;kesu6p*{?JoL1EwPMMQW_E5z`WG9SJFKEH&1AN zMJu%@$!+t2h~5UnoBPp=)Q<7fZ(&+G*}fVRA1QDqT7n3!teuA{rS1q8JF4#0l5s7l z7Q1~E&zn-tw)=ZFv{Jj(&$;kp{tVjhn{W&J3cAL^12q^)mY zOFOd~cGt#qp|AN z-Oe&*Vg1b5wkOn5cdcZ2Pe%{^*J^r%ewBe&?Ol}0YKsChf^YI1_XH)T4a=kt7A+P% zc6jtMR>r*KoW2ZL&AF(w)P3XY+DQEOi^E*yV#5$vav}fyqr&8~ijtN0@)7UsVkTJ? zz+o6>wg=z>j5FIq@DQep4M}uHhM{A>iqV9^FbFm}Oav5yfv}5UTA?(Mz`q8lH3r7O z4i+sCjRDLw6b|;o(6Zb94?bSI%=o_-&t;Kb2L2zkr279R2m}VPf$SHi>~?p@z~OJR z%^od}fkkDS*fx5{!1~%?A|=qjzJ&k8$DhMhs6Zf&f79g8*&{wm`n$WBI1X-}e+JK= zBh+ayFiuu7p5(lu{S^#?Q-O#Vf{Er-RQji>|J>$F3j+ON@js8=4jE?nwSpn%QXslQ XkGaPs4Y>lk0=ggpf!gT*R^|TygM$7| diff --git a/plugin/SqueezeESP32/Graphics.pm b/plugin/SqueezeESP32/Graphics.pm index db58703d..f6a92158 100644 --- a/plugin/SqueezeESP32/Graphics.pm +++ b/plugin/SqueezeESP32/Graphics.pm @@ -21,13 +21,22 @@ my $VISUALIZER_WAVEFORM = 3; __PACKAGE__->mk_accessor('rw', qw(vfdmodel)); } -use Data::Dumper; - sub new { my $class = shift; my $client = shift; my $display = $class->SUPER::new($client); + my $cprefs = $prefs->client($client); + + $cprefs->init( { + width => 128, + small_VU => 15, + spectrum => { scale => 25, + small => { size => 25, band => 5.33 }, + full => { band => 8 }, + }, + } + ); $display->init_accessor( modes => $display->build_modes, @@ -44,14 +53,13 @@ sub modes { =cut sub nmodes { - # -1 for LMS bug workaround - return scalar(@{shift->modes}) - 1; + return scalar($#{shift->modes()}); } sub displayWidth { my $display = shift; my $client = $display->client; - + # if we're showing the always-on visualizer & the current buttonmode # hasn't overridden, then use the playing display mode to index # into the display width, otherwise, it's fullscreen. @@ -88,22 +96,13 @@ sub displayHeight { return 32; } -=comment -sub vfdmodel { - return 'graphic-'.$width.'x32'; -} -=cut - sub build_modes { my $client = shift->client; - print("CLIENT IN BUILD MODE $client\n"); my $cprefs = $prefs->client($client); - my $width = shift || $cprefs->get('width') || $prefs->get('width') || 128; - my $small_VU = shift || $cprefs->get('small_vu') || 0.15; - my $small_spectrum = shift || $cprefs->get('small_spectrum') || 0.25; - my $spectrum_bar = shift || $cprefs->get('spectrum_bar') || { 'small' => 0.1875, 'full' => 0.125 }; - my $spectrum_scale = shift || $cprefs->get('spectrum_scale') || $prefs->get('spectrum_scale') || 50; + my $width = shift || $cprefs->get('width') || 128; + my $small_VU = $cprefs->get('small_VU'); + my $spectrum = $cprefs->get('spectrum'); my @modes = ( # mode 0 @@ -136,14 +135,14 @@ sub build_modes { params => [$VISUALIZER_NONE] }, # mode 7 { desc => ['VISUALIZER_VUMETER_SMALL'], - bar => 0, secs => 0, width => $width, _width => int -($small_VU*$width), + bar => 0, secs => 0, width => $width, _width => int -($small_VU*$width/100), # extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space) - params => [$VISUALIZER_VUMETER, int ($small_VU* $width), 32, int -($small_VU*$width), 0, 2] }, + params => [$VISUALIZER_VUMETER, int ($small_VU*$width/100), 32, int -($small_VU*$width/100), 0, 2] }, # mode 8 { desc => ['VISUALIZER_SPECTRUM_ANALYZER_SMALL'], - bar => 0, secs => 0, width => $width, _width => int -($small_spectrum*$width), + bar => 0, secs => 0, width => $width, _width => int -($spectrum->{small}->{size}*$width/100), # extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space, bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($small_spectrum*$width), 32, int -($small_spectrum*$width), 0, 2, int ($small_spectrum*$width*$spectrum_bar->{small}), $spectrum_scale] }, + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($spectrum->{small}->{size}*$width/100), 32, int -($spectrum->{small}->{size}*$width/100), 0, 2, int ($spectrum->{small}->{size}/100*$width/$spectrum->{small}->{band}), $spectrum->{scale}/100] }, # mode 9 { desc => ['VISUALIZER_VUMETER'], bar => 0, secs => 0, width => $width, @@ -152,7 +151,7 @@ sub build_modes { { desc => ['VISUALIZER_SPECTRUM_ANALYZER'], bar => 0, secs => 0, width => $width, # extra parameters (bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width * $spectrum_bar->{full}), $spectrum_scale] }, + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}/100] }, # mode 11 { desc => ['VISUALIZER_VUMETER', 'AND', 'ELAPSED'], bar => 0, secs => 1, width => $width, @@ -161,7 +160,7 @@ sub build_modes { { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'ELAPSED'], bar => 0, secs => 1, width => $width, # extra parameters (bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width * $spectrum_bar->{full}), $spectrum_scale] }, + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}/100] }, # mode 13 { desc => ['VISUALIZER_VUMETER', 'AND', 'REMAINING'], bar => 0, secs => -1, width => $width, @@ -170,9 +169,7 @@ sub build_modes { { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'REMAINING'], bar => 0, secs => -1, width => $width, # extra parameters (bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width * $spectrum_bar->{full}), $spectrum_scale] }, - # dummy for LMS bug workaround - { desc => [], bar => 0, secs => -1, width => $width,params => [] }, + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}/100] }, ); return \@modes; diff --git a/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/player.html b/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/player.html new file mode 100644 index 00000000..17589674 --- /dev/null +++ b/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/player.html @@ -0,0 +1,28 @@ +[% PROCESS settings/header.html %] + + [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_WIDTH" desc="PLUGIN_SQUEEZEESP32_WIDTH_DESC" %] + + [% prefs.pref_width %] + [% END %] + + [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_SMALL_VU" desc="PLUGIN_SQUEEZEESP32_SMALL_VU_DESC" %] + + [% END %] + + [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE" desc="PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE_DESC" %] + + [% END %] + + [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM" desc="PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_DESC" %] + [% "PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_SIZE" | string %]  + + [% "PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_BAND" | string %]  + + [% END %] + + [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND" desc="PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND_DESC" %] + + [% END %] + + +[% PROCESS settings/footer.html %] diff --git a/plugin/SqueezeESP32/Player.pm b/plugin/SqueezeESP32/Player.pm index e694d03b..946eb303 100644 --- a/plugin/SqueezeESP32/Player.pm +++ b/plugin/SqueezeESP32/Player.pm @@ -25,6 +25,7 @@ sub playerSettingsFrame { if ($id == 0xfe) { $value = (unpack('Cn', $$data_ref))[1]; if ($value > 100 && $value < 400) { + $prefs->client($client)->set('width', $value); $client->display->modes($client->display->build_modes($value)); $client->display->widthOverride(1, $value); $client->update; diff --git a/plugin/SqueezeESP32/PlayerSettings.pm b/plugin/SqueezeESP32/PlayerSettings.pm new file mode 100644 index 00000000..f4fc24b0 --- /dev/null +++ b/plugin/SqueezeESP32/PlayerSettings.pm @@ -0,0 +1,66 @@ +package Plugins::SqueezeESP32::PlayerSettings; + +use strict; +use base qw(Slim::Web::Settings); +use List::Util qw(first); + +use Slim::Utils::Log; +use Slim::Utils::Prefs; + +my $sprefs = preferences('server'); +my $prefs = preferences('plugin.squeezeesp32'); +my $log = logger('plugin.squeezeesp32'); + +sub name { + return Slim::Web::HTTP::CSRF->protectName('PLUGIN_SQUEEZEESP32_PLAYERSETTINGS'); +} + +sub needsClient { + return 1; +} + +sub validFor { + my ($class, $client) = @_; + return $client->model eq 'squeezeesp32'; +} + +sub page { + return Slim::Web::HTTP::CSRF->protectURI('plugins/SqueezeESP32/settings/player.html'); +} + +sub prefs { + my ($class, $client) = @_; + my @prefs = qw(width small_VU spectrum); + return ($prefs->client($client), @prefs); +} + +sub handler { + my ($class, $client, $paramRef) = @_; + + my ($cprefs, @prefs) = $class->prefs($client); + + if ($paramRef->{'saveSettings'}) { + $cprefs->set('small_VU', $paramRef->{'pref_small_VU'}); + my $spectrum = { scale => $paramRef->{'pref_spectrum_scale'}, + small => { size => $paramRef->{'pref_spectrum_small_size'}, + band => $paramRef->{'pref_spectrum_small_band'} }, + full => { band => $paramRef->{'pref_spectrum_full_band'} }, + }; + $cprefs->set('spectrum', $spectrum); + $client->display->modes($client->display->build_modes); + $client->display->update; + } + + # as there is nothing captured, we need to re-set these variables + $paramRef->{'pref_width'} = $cprefs->get('width'); + + # here I don't know why you need to set again spectrum which is a reference + # to a hash. Using $paramRef->{prefs} does not work either. It seems that + # soem are copies of value, some are references, can't figure out.This whole + # logic of "Settings" is beyond me and I really hate it + $paramRef->{'pref_spectrum'} = $cprefs->get('spectrum'); + + return $class->SUPER::handler($client, $paramRef); +} + +1; \ No newline at end of file diff --git a/plugin/SqueezeESP32/Plugin.pm b/plugin/SqueezeESP32/Plugin.pm index e39979bf..5078f66c 100644 --- a/plugin/SqueezeESP32/Plugin.pm +++ b/plugin/SqueezeESP32/Plugin.pm @@ -8,11 +8,6 @@ use Slim::Utils::Log; my $prefs = preferences('plugin.squeezeesp32'); -$prefs->init({ - width => 128, - spectrum_scale => 50, -}); - my $log = Slim::Utils::Log->addLogCategory({ 'category' => 'plugin.squeezeesp32', 'defaultLevel' => 'INFO', @@ -23,10 +18,13 @@ sub initPlugin { my $class = shift; if ( main::WEBUI ) { - require Plugins::SqueezeESP32::Settings; - Plugins::SqueezeESP32::Settings->new; + require Plugins::SqueezeESP32::PlayerSettings; + Plugins::SqueezeESP32::PlayerSettings->new; + + # require Plugins::SqueezeESP32::Settings; + # Plugins::SqueezeESP32::Settings->new; } - + $class->SUPER::initPlugin(@_); Slim::Networking::Slimproto::addPlayerClass($class, 100, 'squeezeesp32', { client => 'Plugins::SqueezeESP32::Player', display => 'Plugins::SqueezeESP32::Graphics' }); $log->info("Added class 100 for SqueezeESP32"); diff --git a/plugin/SqueezeESP32/install.xml b/plugin/SqueezeESP32/install.xml index 812417df..38f410ad 100644 --- a/plugin/SqueezeESP32/install.xml +++ b/plugin/SqueezeESP32/install.xml @@ -10,6 +10,6 @@ PLUGIN_SQUEEZEESP32 PLUGIN_SQUEEZEESP32_DESC Plugins::SqueezeESP32::Plugin - 0.21 + 0.30 Philippe diff --git a/plugin/SqueezeESP32/strings.txt b/plugin/SqueezeESP32/strings.txt index d00ff9b5..1dd9e492 100644 --- a/plugin/SqueezeESP32/strings.txt +++ b/plugin/SqueezeESP32/strings.txt @@ -13,13 +13,43 @@ PLUGIN_SQUEEZEESP32_BANNER_TEXT PLUGIN_SQUEEZEESP32_DESC EN Adds a new player id (100) to enable display with SqueezeESP32 +PLUGIN_SQUEEZEESP32_PLAYERSETTINGS + EN Display (ESP32) + PLUGIN_SQUEEZEESP32_WIDTH EN Screen width + +PLUGIN_SQUEEZEESP32_WIDTH_DESC + EN Width of the display in pixel as reported by the player + +PLUGIN_SQUEEZEESP32_SMALL_VU + EN Small VU size + +PLUGIN_SQUEEZEESP32_SMALL_VU_DESC + EN % of the display used for small VU (right-justified) PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE - EN Spectrum scale - + EN Spectrum scaling + PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE_DESC - EN Sets the scale factor % of spectrum visualizer by halves for better representation. - EN For example, 50 means that 50% of spectrum is displayed in 1/2 of the screen, so it's linear... + EN % of Spectrum displayed in first half of the screen. For example, 50 means that 50% of spectrum is displayed in 1/2 of the screen EN But 25 means that only 25% of spectrum is displayed in 1/2 of the screen, so it's a sort of log + +PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM + EN Small spectrum options + +PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_DESC + EN Size: % of the screen used by small spectrum + EN
Band factor: number of bands is the width of the spectrum screen divided by this factor + +PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_SIZE + EN Size + +PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_BAND + EN Band factor + +PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND + EN Full spectrum band factor + +PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND_DESC + EN The number of bands is the width of the screen divided by this factor diff --git a/plugin/repo.xml b/plugin/repo.xml index 91822b34..ddbdf68f 100644 --- a/plugin/repo.xml +++ b/plugin/repo.xml @@ -4,7 +4,7 @@ https://github.com/sle118/squeezelite-esp32 Philippe - f1955a3c930a0c1809a1d2dac85970905c9b68a0 + a110570f53f24c571492b13cf225cbfd1bdf9a52 philippe_44@outlook.com SqueezeESP32 additional player id (100) http://github.com/sle118/squeezelite-esp32/raw/master/plugin/SqueezeESP32.zip From 1054b20972ed5e3a6a2303533b03e7a921ddd200 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Thu, 5 Mar 2020 19:49:05 -0800 Subject: [PATCH 16/20] Bump version in repo --- plugin/repo.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/repo.xml b/plugin/repo.xml index ddbdf68f..c611d370 100644 --- a/plugin/repo.xml +++ b/plugin/repo.xml @@ -1,7 +1,7 @@ - + https://github.com/sle118/squeezelite-esp32 Philippe a110570f53f24c571492b13cf225cbfd1bdf9a52 From fae613eb332c5d9125bea2381ea8d63379dd4cbb Mon Sep 17 00:00:00 2001 From: philippe44 Date: Thu, 5 Mar 2020 20:19:35 -0800 Subject: [PATCH 17/20] Forgot that scale is an int, not a float --- plugin/SqueezeESP32.zip | Bin 7560 -> 7561 bytes plugin/SqueezeESP32/Graphics.pm | 8 ++++---- plugin/SqueezeESP32/install.xml | 2 +- plugin/repo.xml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugin/SqueezeESP32.zip b/plugin/SqueezeESP32.zip index 743e2fe5c00d4a77c925a6c043dd438cd3979188..871345cc5d83a05d2509da56db6515bef41c97b9 100644 GIT binary patch delta 1915 zcmV->2ZZ>DJBd3NP)h>@6aWAK2mq_0Wl+O5d?FYI008Ha7fXNr?c;WnPE6~&IbzUu z!;#~QTCMvdWb7w1y6tMER(r$E$=I>PVKy5I210VjlJUd9#6Lf#xvuMYwc0C3Ok8u8 zDi{Rk6+K2qh@Ny@G|=X3i263x8^H+S-VE+e2m#N5u8cF^Vj`za6v3j1v@&9 z$7kW0u8SE_O9yIY!Ys){4_7ktjId0Xk+4ES{E4^V5-JqV&qRpj>f3aGP^?yAk${IG zVihaxfnQ~i*hv-F#UA!5CZH%V1mNW~H~2p!k%D*uQK}F3Oo;ks!|(Kn5W3EFVqSG5f{oh z|LZ75FB}#1=oQ{LC*@#Fl^ZN##lQDe35+GlVJAIKxfB)bU3k z#|%t&YR*I<@Zro6q3Jq5(e-;uWQClQ!a*oypr`|>8Jj9f!5QP+v2Et(3GD+m7PXBm zllFw9+9(5Xe4Cvs9zH%HGDWB~?matO6iFj zF`3ii2*jPaNhziMU#^8|I($R5i^RXBdn!byQOKF1oRrWO3_MtFkIM=fM0|h!g3`Bn zU*Q+(NhhnoT)|dVU%fxtP8%~xtq!;|8cTm44`$Pxtdx1~(ZN9#TS2*$hl3Jb@lU0M zW;f2!U?!PpGkzSnq35sY*k2x{JvhO`N<}Egj{hAR%`RZNY|I(Q^7L(&2~1)zVceZT zR6-&uFOVpQY6T|FIl5bf6N%~sW1b+91ZrVZ2ACjl1ehtbAW34I(!_GblW6)gGCl%| zf|J|>83aE0$Vrp=10R2)PJ$(Uz+?+2z!Nwfqw^5>45mTo+8Bj12JD<#HnSsp$_%PQ zk^_`^6FFOLFmDyNWx&x&+Jjb_DFHTTRdP6XnL-3qHIyj9x>`FSO04N9ps^GyUrweq zbRy7_VVF#;4P+wFQYKNiMqm!he=q7;CA^&jE?YD;zGyES_11qo?Hi4gfyrsDl*iJs zM0^YB480q7G(=9Q+bG1B^>?6eIPaF$%4^ozozpiRqucGjtamOLEnjVP7n_wen?|GF z?i#PQ`B=WCx$5QS=*6nuqRWk(F5foN`)f2hMzem>I%&Px=69i-zm?wy0Ba_X8>h!t zl+vbumK#{#mG^%;S$>q+Fx@rY_uBpL+tZIPd#_&`o!7laqlF>gsjz7@Q=8wN(c?8b zo3H=_n%Zt~l6}3c0jL+>a0s%Q$Ny66{=Z6BJ!0v3o);(vkLND{vwA3`*Dw zhCJ{HmxWF5F*s+L*v(~?n`?~_HraM5ioS&w zsBKlKKD8Em=lzb&)>FCo?45^|#vf9ss=|5;A{nB0GnmTX47L~s7zBa_XR4}Sx* zK?aBfe>S3JP#$qb=Mw<{0O|n%01E&B0BLS>bYX04E_iKhZIMe$!$1^;_k#Z+yMT*K zYb!#yohlkc5UC7x;Us=d~+XjVcjSh?a?}6^eUbwvp8~| z>yk^OaTWidi`Vr046yO2TgU|!MNr}gucc?6e|w~;`Mbmt$bYxzP;nt?^(myND$JM5 z8&i8}%vVwv1*D66(sR2*e_K^j6x@eSogD%t^lLvLy-u!x9P~u+W^^t>KBy@dC1p}5 zhBj;pB*?BKIo%R8S4cNc+lR;JoV{$b>@CaKW^oCmO~(CQVMWzGI)&!<8M^~AP*Z7T zG|F0r;1Oy}AcNDMj2zwU z53~9Up9>AEp=D6RHhdx&1^@u(lcpCe1*+T5k!`cr7aju%Hlk%v9&ttI6O;ZLFa_IG z94>B?GaEet`;&1SS^*G~%o}0>Ws@%)S^*`KiX2=4eUs-LTmjjWL>)c`ju!v`002*9 Bn@0cu delta 1914 zcmV-=2Zi{FJBT|MP)h>@6aWAK2mmsiWl$TN267k%008%q7fXNFZ*Mp0#I(MfBL;0Z z967$I)w(}I#(pxR+pbn>wKv?Hj2%lHX0xGSAS8Dz89xk6{PSa)>$;9ttG#l>#5HHB zfKzSK+AJYa5+f4b|o1O?sjt<}e}?p^-R+DW!;tgpTjX9Lyn`$xIzvj^X(clq*m2 z_$)lrb&(OZbf8uy%#u9xa3wR(2+MRC2`ePTpLh!{p+e#OOoUjjzD@TB#cCB633wPH zR)JH%lSF^2E{hC$svpvGmVT2oL|!lRO;SY*saS#rq2tg6{F9s!1FMvirUD9d5Zk{LaiVWaiNU) zUq?|EO`f~EH&>JwjuarLFP1eETl}c_nO~@gE#-fc$`kg9bd1j#!jx&8;ih2f_#=>G z2BteTXQB}JaOQ~6be*3V`aLDGLe5FyAe1stt^=tVn<`7e8ROisZRY0*?E^LzwT z_QXlGQ3l}nHak~-`1pj#6rpIyLx5|1>IjwxM`{A7M1;i|^NUe{q=_1biU*^~twc&O zeMo;J$mwwJ9^A3n(D9iaSAU$|w#iK|r%||?u7gfVjq-PC{}YU3q?&%&QOYDPr6+2{ zWKN4S5O?M#QA+#2Tnp25_=aj1Nqo!jREkcclr!aWQbJoW@L;(;E-UmP^7q#-D1Dpv z6@HL@Y)%&CEv@w&^>VP|=vGjlOU^dOkN}1;#9UN4#6_iVPI4IE-|5Qq7 zcH;lqdBWE1T)3;qFki=lZxI2TW zghW(cAW;t03QU}H47Ug;64eRDJV7D})WW6=kRWgbm?^X%Nn)GQ#B#-xX!lAAfS41WWe;$ret4CvZB(;34oCOoPz1F$-r5*g3UqW=Hmv8B~W( z4p8Pzct!(?J@AQOR>dJ=VO1m>{(_oALv!rM9EvPDzli}tcnZ-2ehzR@@tn4H#1c`O}E z#J7;n(7SO*L*#_IjY51`e+T-8^KNOayk@Q4IepVHy50WEdgp@C^3_Inu~}KOX*BBX zuJLMHjOAOJt6px7UaaaZy4)z}@@+G{zvf2AXx2|!C#^Tz;x3f)w~G4!V9n%la0s%Q$NyC)l~de zKy9l+`bdbj688X0_Mc|p+T*kf-`Cdf+Q=&HP<=c4aasNRDd(H{9)>qfcV;!;Au zo{qD>J1tKyr=YS61-JF=|0Jcl8!0y$_qXO{|Ic{&W9R-GYO)#iQoZ?qkW4mPKKu={ zK?aBff0UhNQ15gJjuHU?0O|n%01E&B0BLS>bYX04E_iKhZIMe$#4r?v_k#bSyMl{m zbVh`7C!=T)L8KDKg_{!F>ln!Yt1@U+yCpHm#D;9<38bujBJ%5l7B* zU2$nNuH$cX@rItB0Ja`=2f3i42r7K%jr7cOe~%P3f0I}N`RfiGDlR0gKZF!@jm2tp zZ5l6)`AkZqfb{V|dTw{Xf7qD%_K z(1uNc1le~a$6JEt3Tghhy?=Nr*z-2aUbBql%L^b~GVSk5E9&miF|@eL*e#HenoBEF zGc__4m(5OSm!|BiLHxn~O_~likb#<}-;c{IlH~$Orw-*xi`*Ma^YMRyj81nl^6zH9 zv-=C53k@=yWl$TN267k%008%srxz>*9?K;JXtUTC9s>!Kon=t(bP0|Ulm8kp1xJR9 zk7<)M8$AK~lX4qc0S}YS8)5-ulQ0}w0VI=)99#i>ljs~=0oap89X ['VISUALIZER_SPECTRUM_ANALYZER_SMALL'], bar => 0, secs => 0, width => $width, _width => int -($spectrum->{small}->{size}*$width/100), # extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space, bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($spectrum->{small}->{size}*$width/100), 32, int -($spectrum->{small}->{size}*$width/100), 0, 2, int ($spectrum->{small}->{size}/100*$width/$spectrum->{small}->{band}), $spectrum->{scale}/100] }, + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($spectrum->{small}->{size}*$width/100), 32, int -($spectrum->{small}->{size}*$width/100), 0, 2, int ($spectrum->{small}->{size}/100*$width/$spectrum->{small}->{band}), $spectrum->{scale}] }, # mode 9 { desc => ['VISUALIZER_VUMETER'], bar => 0, secs => 0, width => $width, @@ -151,7 +151,7 @@ sub build_modes { { desc => ['VISUALIZER_SPECTRUM_ANALYZER'], bar => 0, secs => 0, width => $width, # extra parameters (bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}/100] }, + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] }, # mode 11 { desc => ['VISUALIZER_VUMETER', 'AND', 'ELAPSED'], bar => 0, secs => 1, width => $width, @@ -160,7 +160,7 @@ sub build_modes { { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'ELAPSED'], bar => 0, secs => 1, width => $width, # extra parameters (bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}/100] }, + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] }, # mode 13 { desc => ['VISUALIZER_VUMETER', 'AND', 'REMAINING'], bar => 0, secs => -1, width => $width, @@ -169,7 +169,7 @@ sub build_modes { { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'REMAINING'], bar => 0, secs => -1, width => $width, # extra parameters (bars) - params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}/100] }, + params => [$VISUALIZER_SPECTRUM_ANALYZER, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] }, ); return \@modes; diff --git a/plugin/SqueezeESP32/install.xml b/plugin/SqueezeESP32/install.xml index 38f410ad..338f4283 100644 --- a/plugin/SqueezeESP32/install.xml +++ b/plugin/SqueezeESP32/install.xml @@ -10,6 +10,6 @@ PLUGIN_SQUEEZEESP32 PLUGIN_SQUEEZEESP32_DESC Plugins::SqueezeESP32::Plugin - 0.30 + 0.31 Philippe diff --git a/plugin/repo.xml b/plugin/repo.xml index c611d370..45a2e536 100644 --- a/plugin/repo.xml +++ b/plugin/repo.xml @@ -1,10 +1,10 @@ - + https://github.com/sle118/squeezelite-esp32 Philippe - a110570f53f24c571492b13cf225cbfd1bdf9a52 + 6dc35a0f9f9b287d205f7532cbb642b08407a284 philippe_44@outlook.com SqueezeESP32 additional player id (100) http://github.com/sle118/squeezelite-esp32/raw/master/plugin/SqueezeESP32.zip From 8e95bd3dd28f6cded0feec261993f508c1c220f6 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sat, 7 Mar 2020 14:01:53 -0800 Subject: [PATCH 18/20] AirPlay fix & misc - Spectrum scale fix - Initialize more display parameters - Reboot after 30s of no connection - Reboot after IP address change --- components/display/SSD1306.c | 13 ++++--- components/display/SSD132x.c | 20 +++++++++-- components/display/display.c | 4 +-- components/raop/raop.c | 7 ++-- components/raop/rtp.c | 58 ++++++++++++++++++------------ components/raop/rtp.h | 3 +- components/raop/util.c | 2 +- components/services/monitor.c | 8 +++-- components/squeezelite/display.c | 6 ++-- components/squeezelite/embedded.h | 17 +++++---- components/squeezelite/slimproto.c | 20 +++++++---- main/cmd_squeezelite.c | 19 +++++----- main/esp_app_main.c | 12 +++++++ 13 files changed, 127 insertions(+), 62 deletions(-) diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c index fec88996..487dc84e 100644 --- a/components/display/SSD1306.c +++ b/components/display/SSD1306.c @@ -74,7 +74,7 @@ static void Update( struct GDS_Device* Device ) { CurrentPage = p + 1; // actual write - Device->WriteData( Device, Private->Shadowbuffer + p*width + first, last - first + 1); + Device->WriteData( Device, Private->Shadowbuffer + p*width + first, last - first + 1 ); } } #else @@ -114,6 +114,10 @@ static bool Init( struct GDS_Device* Device ) { // charge pump regulator, do direct init Device->WriteCommand( Device, 0x8D ); Device->WriteCommand( Device, 0x14 ); + + // set Clocks + Device->WriteCommand( Device, 0xD5 ); + Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 ); // COM pins HW config (alternative:EN if 64, DIS if 32, remap:DIS) - some display might need something different Device->WriteCommand( Device, 0xDA ); @@ -122,6 +126,10 @@ static bool Init( struct GDS_Device* Device ) { // MUX Ratio Device->WriteCommand( Device, 0xA8 ); Device->WriteCommand( Device, Device->Height - 1); + // Page & GDDRAM Start Column High/Low + Device->WriteCommand( Device, 0x00 ); + Device->WriteCommand( Device, 0x10 ); + Device->WriteCommand( Device, 0xB0 ); // Display Offset Device->WriteCommand( Device, 0xD3 ); Device->WriteCommand( Device, 0 ); @@ -133,9 +141,6 @@ static bool Init( struct GDS_Device* Device ) { Device->SetHFlip( Device, false ); // no Display Inversion Device->WriteCommand( Device, 0xA6 ); - // set Clocks - Device->WriteCommand( Device, 0xD5 ); - Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 ); // set Adressing Mode Horizontal Device->WriteCommand( Device, 0x20 ); Device->WriteCommand( Device, 0 ); diff --git a/components/display/SSD132x.c b/components/display/SSD132x.c index f7ed2b3b..e68a4d74 100644 --- a/components/display/SSD132x.c +++ b/components/display/SSD132x.c @@ -126,6 +126,7 @@ static void Update1( struct GDS_Device* Device ) { // not sure the compiler does not have to redo all calculation in for loops, so local it is int width = Device->Width / 8, rows = Device->Height; uint8_t *optr = Private->Shadowbuffer, *iptr = Device->Framebuffer; + int CurrentRow = -1, FirstCol = -1, LastCol = -1; // by row, find first and last columns that have been updated for (int r = 0; r < rows; r++) { @@ -140,9 +141,22 @@ static void Update1( struct GDS_Device* Device ) { // now update the display by "byte rows" if (first--) { - SetColumnAddress( Device, first, last ); - SetRowAddress( Device, r, r); - Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1); + // only set column when useful, saves a fair bit of CPU + if (first > FirstCol && first <= FirstCol + 4 && last < LastCol && last >= LastCol - 4) { + first = FirstCol; + last = LastCol; + } else { + SetColumnAddress( Device, first, last ); + FirstCol = first; + LastCol = last; + } + + // Set row only when needed, otherwise let auto-increment work + if (r != CurrentRow) SetRowAddress( Device, r, Device->Height - 1 ); + CurrentRow = r + 1; + + // actual write + Device->WriteData( Device, Private->Shadowbuffer + r*width + first, last - first + 1 ); } } #else diff --git a/components/display/display.c b/components/display/display.c index ddc79a0a..889a91c1 100644 --- a/components/display/display.c +++ b/components/display/display.c @@ -201,8 +201,8 @@ static void displayer_task(void *args) { displayer.tick = tick; displayer.elapsed += elapsed / 1000; xSemaphoreGive(displayer.mutex); - if (displayer.elapsed < 3600) sprintf(counter, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60); - else sprintf(counter, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60); + if (displayer.elapsed < 3600) snprintf(counter, 16, "%5u:%02u", displayer.elapsed / 60, displayer.elapsed % 60); + else snprintf(counter, 16, "%2u:%02u:%02u", displayer.elapsed / 3600, (displayer.elapsed % 3600) / 60, displayer.elapsed % 60); GDS_TextLine(display, 1, GDS_TEXT_RIGHT, (GDS_TEXT_CLEAR | GDS_TEXT_CLEAR_EOL) | GDS_TEXT_UPDATE, counter); timer_sleep = 1000; } else timer_sleep = max(1000 - elapsed, 0); diff --git a/components/raop/raop.c b/components/raop/raop.c index ceed0aa0..a01303e1 100644 --- a/components/raop/raop.c +++ b/components/raop/raop.c @@ -508,7 +508,7 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) pthread_create(&ctx->active_remote.thread, NULL, &search_remote, ctx); #else ctx->active_remote.running = true; - ctx->active_remote.destroy_mutex = xSemaphoreCreateMutex(); + ctx->active_remote.destroy_mutex = xSemaphoreCreateBinary(); ctx->active_remote.xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); ctx->active_remote.thread = xTaskCreateStatic( (TaskFunction_t) search_remote, "search_remote", SEARCH_STACK_SIZE, ctx, ESP_TASK_PRIO_MIN + 1, ctx->active_remote.xStack, ctx->active_remote.xTaskBuffer); #endif @@ -570,7 +570,10 @@ static bool handle_rtsp(raop_ctx_t *ctx, int sock) if ((p = strcasestr(buf, "rtptime")) != NULL) sscanf(p, "%*[^=]=%u", &rtptime); // only send FLUSH if useful (discards frames above buffer head and top) - if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime)) success = ctx->cmd_cb(RAOP_FLUSH); + if (ctx->rtp && rtp_flush(ctx->rtp, seqno, rtptime, true)) { + success = ctx->cmd_cb(RAOP_FLUSH); + rtp_flush_release(ctx->rtp); + } } else if (!strcmp(method, "TEARDOWN")) { diff --git a/components/raop/rtp.c b/components/raop/rtp.c index 0a3b6b3f..ff831884 100644 --- a/components/raop/rtp.c +++ b/components/raop/rtp.c @@ -267,25 +267,25 @@ rtp_resp_t rtp_init(struct in_addr host, int latency, char *aeskey, char *aesiv, resp.cport = ctx->rtp_sockets[CONTROL].lport; resp.tport = ctx->rtp_sockets[TIMING].lport; resp.aport = ctx->rtp_sockets[DATA].lport; - - if (rc) { - ctx->running = true; + + ctx->running = true; + #ifdef WIN32 - pthread_create(&ctx->thread, NULL, rtp_thread_func, (void *) ctx); + pthread_create(&ctx->thread, NULL, rtp_thread_func, (void *) ctx); #else - // xTaskCreate((TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_TASK_SIZE, ctx, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1 , &ctx->thread); - ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); - ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_STACK_SIZE, ctx, - CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, ctx->xStack, ctx->xTaskBuffer ); + ctx->xTaskBuffer = (StaticTask_t*) heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); + ctx->thread = xTaskCreateStatic( (TaskFunction_t) rtp_thread_func, "RTP_thread", RTP_STACK_SIZE, ctx, + CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT + 1, ctx->xStack, ctx->xTaskBuffer ); #endif - } else { + + // cleanup everything if we failed + if (!rc) { LOG_ERROR("[%p]: cannot start RTP", ctx); rtp_end(ctx); ctx = NULL; - } - - resp.ctx = ctx; - + } + + resp.ctx = ctx; return resp; } @@ -327,7 +327,7 @@ void rtp_end(rtp_t *ctx) } /*---------------------------------------------------------------------------*/ -bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime) +bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime, bool exit_locked) { bool rc = true; u32_t now = gettime_ms(); @@ -340,7 +340,7 @@ bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime) buffer_reset(ctx->audio_buffer); ctx->playing = false; ctx->flush_seqno = seqno; - pthread_mutex_unlock(&ctx->ab_mutex); + if (!exit_locked) pthread_mutex_unlock(&ctx->ab_mutex); } LOG_INFO("[%p]: flush %hu %u", ctx, seqno, rtptime); @@ -349,8 +349,13 @@ bool rtp_flush(rtp_t *ctx, unsigned short seqno, unsigned int rtptime) } /*---------------------------------------------------------------------------*/ -void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime) -{ +void rtp_flush_release(rtp_t *ctx) { + pthread_mutex_unlock(&ctx->ab_mutex); +} + + +/*---------------------------------------------------------------------------*/ +void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime) { ctx->record.seqno = seqno; ctx->record.rtptime = rtptime; ctx->record.time = gettime_ms(); @@ -576,7 +581,7 @@ static void *rtp_thread_func(void *arg) { while (ctx->running) { ssize_t plen; char type; - socklen_t rtp_client_len = sizeof(struct sockaddr_storage); + socklen_t rtp_client_len = sizeof(struct sockaddr_in); int idx = 0; char *pktp = packet; struct timeval timeout = {0, 100*1000}; @@ -589,14 +594,18 @@ static void *rtp_thread_func(void *arg) { for (i = 0; i < 3; i++) if (FD_ISSET(ctx->rtp_sockets[i].sock, &fds)) idx = i; - plen = recvfrom(ctx->rtp_sockets[idx].sock, packet, MAX_PACKET, 0, (struct sockaddr*) &ctx->rtp_host, &rtp_client_len); + plen = recvfrom(ctx->rtp_sockets[idx].sock, packet, MAX_PACKET, MSG_DONTWAIT, (struct sockaddr*) &ctx->rtp_host, &rtp_client_len); if (!ntp_sent) { LOG_WARN("[%p]: NTP request not send yet", ctx); ntp_sent = rtp_request_timing(ctx); } - if (plen < 0) continue; + if (plen <= 0) { + LOG_WARN("Nothing received on a readable socket %d", plen); + continue; + } + assert(plen <= MAX_PACKET); type = packet[1] & ~0x80; @@ -715,6 +724,11 @@ static void *rtp_thread_func(void *arg) { break; } + + default: { + LOG_WARN("Unknown packet received %x", (int) type); + break; + } } } @@ -756,7 +770,7 @@ static bool rtp_request_timing(rtp_t *ctx) { host.sin_port = htons(ctx->rtp_sockets[TIMING].rport); - if (sizeof(req) != sendto(ctx->rtp_sockets[TIMING].sock, req, sizeof(req), 0, (struct sockaddr*) &host, sizeof(host))) { + if (sizeof(req) != sendto(ctx->rtp_sockets[TIMING].sock, req, sizeof(req), MSG_DONTWAIT, (struct sockaddr*) &host, sizeof(host))) { LOG_WARN("[%p]: SENDTO failed (%s)", ctx, strerror(errno)); } @@ -782,7 +796,7 @@ static bool rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last) { ctx->rtp_host.sin_port = htons(ctx->rtp_sockets[CONTROL].rport); - if (sizeof(req) != sendto(ctx->rtp_sockets[CONTROL].sock, req, sizeof(req), 0, (struct sockaddr*) &ctx->rtp_host, sizeof(ctx->rtp_host))) { + if (sizeof(req) != sendto(ctx->rtp_sockets[CONTROL].sock, req, sizeof(req), MSG_DONTWAIT, (struct sockaddr*) &ctx->rtp_host, sizeof(ctx->rtp_host))) { LOG_WARN("[%p]: SENDTO failed (%s)", ctx, strerror(errno)); } diff --git a/components/raop/rtp.h b/components/raop/rtp.h index 554690ad..ebf734f5 100644 --- a/components/raop/rtp.h +++ b/components/raop/rtp.h @@ -14,7 +14,8 @@ rtp_resp_t rtp_init(struct in_addr host, int latency, short unsigned pCtrlPort, short unsigned pTimingPort, raop_cmd_cb_t cmd_cb, raop_data_cb_t data_cb); void rtp_end(struct rtp_s *ctx); -bool rtp_flush(struct rtp_s *ctx, unsigned short seqno, unsigned rtptime); +bool rtp_flush(struct rtp_s *ctx, unsigned short seqno, unsigned rtptime, bool exit_locked); +void rtp_flush_release(struct rtp_s *ctx); void rtp_record(struct rtp_s *ctx, unsigned short seqno, unsigned rtptime); void rtp_metadata(struct rtp_s *ctx, struct metadata_s *metadata); diff --git a/components/raop/util.c b/components/raop/util.c index 2b7f3a17..13744409 100644 --- a/components/raop/util.c +++ b/components/raop/util.c @@ -401,7 +401,7 @@ bool http_parse(int sock, char *method, key_data_t *rkd, char **body, int *len) } if (*len) { - int size = 0; + int size = 0; *body = malloc(*len + 1); while (*body && size < *len) { diff --git a/components/services/monitor.c b/components/services/monitor.c index 4b56dbe5..1fb246f1 100644 --- a/components/services/monitor.c +++ b/components/services/monitor.c @@ -23,6 +23,7 @@ #include "accessors.h" #define MONITOR_TIMER (10*1000) +#define SCRATCH_SIZE 256 static const char *TAG = "monitor"; @@ -54,7 +55,7 @@ static void task_stats( void ) { current.tasks = malloc( current.n * sizeof( TaskStatus_t ) ); current.n = uxTaskGetSystemState( current.tasks, current.n, ¤t.total ); - static EXT_RAM_ATTR char scratch[128+1]; + static EXT_RAM_ATTR char scratch[SCRATCH_SIZE]; *scratch = '\0'; #ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS @@ -63,7 +64,8 @@ static void task_stats( void ) { for(int i = 0, n = 0; i < current.n; i++ ) { for (int j = 0; j < previous.n; j++) { if (current.tasks[i].xTaskNumber == previous.tasks[j].xTaskNumber) { - n += sprintf(scratch + n, "%16s %2u%% s:%5u", current.tasks[i].pcTaskName, + n += snprintf(scratch + n, SCRATCH_SIZE - n, "%16s (%u) %2u%% s:%5u", current.tasks[i].pcTaskName, + current.tasks[i].eCurrentState, 100 * (current.tasks[i].ulRunTimeCounter - previous.tasks[j].ulRunTimeCounter) / elapsed, current.tasks[i].usStackHighWaterMark); if (i % 3 == 2 || i == current.n - 1) { @@ -72,7 +74,7 @@ static void task_stats( void ) { } break; } - } + } } #else for (int i = 0, n = 0; i < current.n; i ++) { diff --git a/components/squeezelite/display.c b/components/squeezelite/display.c index 3923467f..544bdf13 100644 --- a/components/squeezelite/display.c +++ b/components/squeezelite/display.c @@ -100,7 +100,7 @@ static struct { #define SB_HEIGHT 32 // lenght are number of frames, i.e. 2 channels of 16 bits -#define FFT_LEN_BIT 6 +#define FFT_LEN_BIT 7 #define FFT_LEN (1 << FFT_LEN_BIT) #define RMS_LEN_BIT 6 #define RMS_LEN (1 << RMS_LEN_BIT) @@ -739,10 +739,10 @@ static void visu_update(void) { */ void spectrum_limits(int min, int n, int pos) { if (n / 2) { - int step = ((DISPLAY_BW - min) * visu.spectrum_scale * 2) / n; + int step = ((DISPLAY_BW - min) * visu.spectrum_scale) / (n/2); visu.bars[pos].limit = min + step; for (int i = 1; i < n/2; i++) visu.bars[pos+i].limit = visu.bars[pos+i-1].limit + step; - spectrum_limits(visu.bars[pos + n/2 - 1].limit, n/2, pos + n/2); + spectrum_limits(visu.bars[pos + n/2 - 1].limit, n - n/2, pos + n/2); } else { visu.bars[pos].limit = DISPLAY_BW; } diff --git a/components/squeezelite/embedded.h b/components/squeezelite/embedded.h index 90926dca..14769aef 100644 --- a/components/squeezelite/embedded.h +++ b/components/squeezelite/embedded.h @@ -6,6 +6,7 @@ /* must provide - mutex_create_p - pthread_create_name + - register_xxx (see below) - stack size - s16_t, s32_t, s64_t and u64_t - PLAYER_ID / custom_player_id @@ -16,6 +17,11 @@ - EXT_BSS recommended to add platform specific include(s) here */ + +typedef int16_t s16_t; +typedef int32_t s32_t; +typedef int64_t s64_t; +typedef unsigned long long u64_t; #ifndef PTHREAD_STACK_MIN #define PTHREAD_STACK_MIN 256 @@ -26,18 +32,17 @@ #define OUTPUT_THREAD_STACK_SIZE 6 * 1024 #define IR_THREAD_STACK_SIZE 6 * 1024 +// number of 5s times search for a server will happen beforee slimproto exits (0 = no limit) +#define MAX_SERVER_RETRIES 5 + // or can be as simple as #define PLAYER_ID 100 -#define PLAYER_ID custom_player_id; +#define PLAYER_ID custom_player_id extern u8_t custom_player_id; #define BASE_CAP "Model=squeezeesp32,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Firmware=" VERSION +// to force some special buffer attribute #define EXT_BSS __attribute__((section(".ext_ram.bss"))) -typedef int16_t s16_t; -typedef int32_t s32_t; -typedef int64_t s64_t; -typedef unsigned long long u64_t; - // all exit() calls are made from main thread (or a function called in main thread) #define exit(code) { int ret = code; pthread_exit(&ret); } #define gettime_ms _gettime_ms_ diff --git a/components/squeezelite/slimproto.c b/components/squeezelite/slimproto.c index af7f7bde..3c55ec43 100644 --- a/components/squeezelite/slimproto.c +++ b/components/squeezelite/slimproto.c @@ -773,7 +773,7 @@ void wake_controller(void) { wake_signal(wake_e); } -in_addr_t discover_server(char *default_server) { +in_addr_t discover_server(char *default_server, int max) { struct sockaddr_in d; struct sockaddr_in s; char buf[32], port_d[] = "JSON", clip_d[] = "CLIP"; @@ -827,7 +827,7 @@ in_addr_t discover_server(char *default_server) { server_addr(default_server, &s.sin_addr.s_addr, &port); } - } while (s.sin_addr.s_addr == 0 && running); + } while (s.sin_addr.s_addr == 0 && running && (!max || --max)); closesocket(disc_sock); @@ -858,7 +858,7 @@ void slimproto(log_level level, char *server, u8_t mac[6], const char *name, con } if (!slimproto_ip) { - slimproto_ip = discover_server(server); + slimproto_ip = discover_server(server, 0); } if (!slimproto_port) { @@ -937,10 +937,18 @@ void slimproto(log_level level, char *server, u8_t mac[6], const char *name, con sleep(5); } - // rediscover server if it was not set at startup +#if EMBEDDED + // in embedded we give up after a while no matter what + if (++failed_connect > 5 && !server) { + slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, MAX_SERVER_RETRIES); + if (!slimproto_ip) return; + } else if (MAX_SERVER_RETRIES && failed_connect > 5 * MAX_SERVER_RETRIES) return; +#else + // rediscover server if it was not set at startup or exit if (!server && ++failed_connect > 5) { - slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL); - } + slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, 0); + } +#endif } else { diff --git a/main/cmd_squeezelite.c b/main/cmd_squeezelite.c index ae958166..0399ae2f 100644 --- a/main/cmd_squeezelite.c +++ b/main/cmd_squeezelite.c @@ -13,9 +13,8 @@ #include "freertos/event_groups.h" #include "pthread.h" #include "platform_esp32.h" -#include "nvs.h" -#include "nvs_flash.h" -//extern char current_namespace[]; +#include "config.h" + static const char * TAG = "squeezelite_cmd"; #define SQUEEZELITE_THREAD_STACK_SIZE (6*1024) extern int main(int argc, char **argv); @@ -36,7 +35,7 @@ static void * squeezelite_runner_thread(){ main(thread_parms.argc,thread_parms.argv); return NULL; } -#define ADDITIONAL_SQUEEZELILTE_ARGS 5 +#define ADDITIONAL_SQUEEZELITE_ARGS 5 static void * squeezelite_thread(){ int * exit_code; static bool isRunning=false; @@ -45,9 +44,6 @@ static void * squeezelite_thread(){ return NULL; } isRunning=true; -// Let's not wait on WiFi to allow squeezelite to run in bluetooth mode -// ESP_LOGI(TAG,"Waiting for WiFi."); -// while(!wait_for_wifi()){usleep(100000);}; ESP_LOGV(TAG ,"Number of args received: %u",thread_parms.argc ); ESP_LOGV(TAG ,"Values:"); for(int i = 0;i Date: Sat, 7 Mar 2020 14:06:59 -0800 Subject: [PATCH 19/20] display register remove - release --- components/display/SSD1306.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/display/SSD1306.c b/components/display/SSD1306.c index 487dc84e..e1280b8b 100644 --- a/components/display/SSD1306.c +++ b/components/display/SSD1306.c @@ -127,9 +127,11 @@ static bool Init( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xA8 ); Device->WriteCommand( Device, Device->Height - 1); // Page & GDDRAM Start Column High/Low + /* Device->WriteCommand( Device, 0x00 ); Device->WriteCommand( Device, 0x10 ); Device->WriteCommand( Device, 0xB0 ); + */ // Display Offset Device->WriteCommand( Device, 0xD3 ); Device->WriteCommand( Device, 0 ); From ca702d1b90f7cd6a2f92138f4e6d96d9f1d50e03 Mon Sep 17 00:00:00 2001 From: philippe44 Date: Sat, 7 Mar 2020 18:14:11 -0800 Subject: [PATCH 20/20] filter out AirPlay erroneous sync point - release --- components/raop/rtp.c | 1 + components/squeezelite/decode_external.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/components/raop/rtp.c b/components/raop/rtp.c index ff831884..de3f41a4 100644 --- a/components/raop/rtp.c +++ b/components/raop/rtp.c @@ -49,6 +49,7 @@ #ifdef WIN32 #include #include "alac_wrapper.h" +#define MSG_DONTWAIT 0 #else #include "esp_pthread.h" #include "esp_system.h" diff --git a/components/squeezelite/decode_external.c b/components/squeezelite/decode_external.c index 23c0fc08..6143764a 100644 --- a/components/squeezelite/decode_external.c +++ b/components/squeezelite/decode_external.c @@ -206,6 +206,10 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args) // in how many ms will the most recent block play ms = ((u64_t) ((_buf_used(outputbuf) - raop_sync.len) / BYTES_PER_FRAME + output.device_frames + output.frames_in_process) * 1000) / RAOP_SAMPLE_RATE - (now - output.updated); raop_sync.error[raop_sync.idx] = (raop_sync.playtime - now) - ms; + if (abs(raop_sync.error[raop_sync.idx]) > 1000) { + LOG_INFO("erroneous sync point %d", raop_sync.error[raop_sync.idx]); + raop_sync.error[raop_sync.idx] = raop_sync.error[raop_sync.idx] > 0 ? 1000 : -1000; + } sync_nb = SYNC_NB; LOG_DEBUG("head local:%u, remote:%u (delta:%d)", ms, raop_sync.playtime - now, raop_sync.error[raop_sync.idx]); LOG_DEBUG("obuf:%u, sync_len:%u, devframes:%u, inproc:%u", _buf_used(outputbuf), raop_sync.len, output.device_frames, output.frames_in_process);