Explorar el Código

Merge remote-tracking branch 'origin/master' into master-cmake

Conflicts:
	components/raop/rtp.c
Sebastien hace 5 años
padre
commit
c545c96fc1

+ 2 - 0
README.md

@@ -90,6 +90,8 @@ The NVS parameter "metadata_config" sets how metadata is displayed for AirPlay a
 
 - 'format' can contain free text and any of the 3 keywords %artist%, %album%, %title%. Using that format string, the keywords are replaced by their value to build the string to be displayed. Note that the plain text following a keyword that happens to be empty during playback of a track will be removed. For example, if you have set format=%artist% - %title% and there is no artist in the metadata then only <title> will be displayed not " - <title>".
 
+You can install the excellent plugin "Music Information Screen" which is super useful to tweak the layout for these small displays.
+
 ### Set GPIO
 The parameter "set_GPIO" is use to assign GPIO to various functions.
 

+ 2 - 0
components/display/core/gds.c

@@ -158,7 +158,9 @@ bool GDS_Init( struct GDS_Device* Device ) {
 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 ); }
+void GDS_SetDirty( struct GDS_Device* Device ) { Device->Dirty = true; }
 int	GDS_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
 int	GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; }
+int	GDS_GetDepth( struct GDS_Device* Device ) { return Device->Depth; }
 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 ); }

+ 2 - 0
components/display/core/gds.h

@@ -35,8 +35,10 @@ void 	GDS_DisplayOff( struct GDS_Device* Device );
 void 	GDS_Update( struct GDS_Device* Device );
 void 	GDS_SetHFlip( struct GDS_Device* Device, bool On );
 void 	GDS_SetVFlip( struct GDS_Device* Device, bool On );
+void 	GDS_SetDirty( struct GDS_Device* Device );
 int 	GDS_GetWidth( struct GDS_Device* Device );
 int 	GDS_GetHeight( struct GDS_Device* Device );
+int 	GDS_GetDepth( struct GDS_Device* Device );
 void 	GDS_ClearExt( struct GDS_Device* Device, bool full, ...);
 void 	GDS_Clear( struct GDS_Device* Device, int Color );
 void 	GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );

+ 99 - 17
components/display/core/gds_image.c

@@ -133,45 +133,126 @@ void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height) {
 }	
 
 /****************************************************************************************
- * Simply draw a RGB565 image
- * monoschrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
+ * Simply draw a RGB 16bits image
+ * monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
  * grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
  */
 void GDS_DrawRGB16( struct GDS_Device* Device, uint16_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
 	if (Device->DrawRGB16) {
-		Device->DrawRGB16( Device, x, y, Width, Height, RGB_Mode, Image );
+		Device->DrawRGB16( Device, Image, x, y, Width, Height, RGB_Mode );
 	} else {
-		int Scale = Device->Depth < 5 ? 5 - Device->Depth : 0;
 		switch(RGB_Mode) {
 		case GDS_RGB565:
-			for (int c = 0; c < Width; c++) {
+			// 6 bits pixels to be placed. Use a linearized structure for a bit of optimization
+			if (Device->Depth < 6) {
+				int Scale = 6 - Device->Depth;
+				for (int r = 0; r < Height; r++) {
+					for (int c = 0; c < Width; c++) {
+						int pixel = *Image++;
+						pixel = ((((pixel & 0x1f) * 11) << 1) + ((pixel >> 5) & 0x3f) * 59 + (((pixel >> 11) * 30) << 1) + 1) / 100;
+						GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
+					}	
+				}	
+			} else {
+				int Scale = Device->Depth - 6;
 				for (int r = 0; r < Height; r++) {
-					int pixel = Image[Width*r + c];
-					pixel = ((pixel & 0x1f) * 11 + ((((pixel >> 5) & 0x3f)  * 59) >> 1) + (pixel >> 11) * 30) / 100;
-					GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
+					for (int c = 0; c < Width; c++) {
+						int pixel = *Image++;
+						pixel = ((((pixel & 0x1f) * 11) << 1) + ((pixel >> 5) & 0x3f) * 59 + (((pixel >> 11) * 30) << 1) + 1) / 100;
+						GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
+					}	
 				}	
 			}	
 			break;
 		case GDS_RGB555:
-			for (int c = 0; c < Width; c++) {
+			// 5 bits pixels to be placed Use a linearized structure for a bit of optimization
+			if (Device->Depth < 5) {
+				int Scale = 5 - Device->Depth;
 				for (int r = 0; r < Height; r++) {
-					int pixel = Image[Width*r + c];
-					pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f)  * 59 + (pixel >> 10) * 30) / 100;
-					GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
+					for (int c = 0; c < Width; c++) {
+						int pixel = *Image++;
+						pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
+						GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
+					}	
 				}	
+			} else {
+				int Scale = Device->Depth - 5;
+				for (int r = 0; r < Height; r++) {
+					for (int c = 0; c < Width; c++) {
+						int pixel = *Image++;
+						pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
+						GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
+					}	
+				}		
 			}	
 			break;
 		case GDS_RGB444:
-			for (int c = 0; c < Width; c++) {
+			// 4 bits pixels to be placed 
+			if (Device->Depth < 4) {
+				int Scale = 4 - Device->Depth;
 				for (int r = 0; r < Height; r++) {
-					int pixel = Image[Width*r + c];
-					pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f)  * 59 + (pixel >> 8) * 30;
-					GDS_DrawPixel( Device, c + x, r + y, pixel >> (Scale - 1));
+					for (int c = 0; c < Width; c++) {
+						int pixel = *Image++;
+						pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
+						GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
+					}	
+				}	
+			} else {
+				int Scale = Device->Depth - 4;
+				for (int r = 0; r < Height; r++) {
+					for (int c = 0; c < Width; c++) {
+						int pixel = *Image++;
+						pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
+						GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
+					}	
 				}	
 			}	
 			break;				
 		}
-	}	 
+	}	
+	
+	Device->Dirty = true;	
+}
+
+/****************************************************************************************
+ * Simply draw a RGB 8 bits  image (R:3,G:3,B:2) or plain grayscale
+ * monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
+ * grayscale (0.3 * R) + (0.59 * G) + (0.11 * B) )
+ */
+void GDS_DrawRGB8( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
+	if (Device->DrawRGB8) {
+		Device->DrawRGB8( Device, Image, x, y, Width, Height, RGB_Mode );
+	} else if (RGB_Mode == GDS_GRAYSCALE) {
+		// 8 bits pixels
+		int Scale = 8 - Device->Depth;
+		for (int r = 0; r < Height; r++) {
+			for (int c = 0; c < Width; c++) {
+				GDS_DrawPixel( Device, c + x, r + y, *Image++ >> Scale);
+			}	
+		}	
+	} else if (Device->Depth < 3) {
+		// 3 bits pixels to be placed 
+		int Scale = 3 - Device->Depth;
+		for (int r = 0; r < Height; r++) {
+			for (int c = 0; c < Width; c++) {
+				int pixel = *Image++;
+				pixel = ((((pixel & 0x3) * 11) << 1) + ((pixel >> 2) & 0x7) * 59 + (pixel >> 5) * 30 + 1) / 100;
+				GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
+			}	
+		}	
+	} else {	
+		// 3 bits pixels to be placed 
+		int Scale = Device->Depth  - 3;
+		for (int r = 0; r < Height; r++) {
+			for (int c = 0; c < Width; c++) {
+				int pixel = *Image++;
+				pixel = ((((pixel & 0x3) * 11) << 1) + ((pixel >> 2) & 0x7) * 59 + (pixel >> 5) * 30 + 1) / 100;
+				GDS_DrawPixel( Device, c + x, r + y, pixel << Scale);
+			}	
+		}	
+	}
+	
+	Device->Dirty = true;		
 }	
 
 //Decode the embedded image into pixel lines that can be used with the rest of the logic.
@@ -228,6 +309,7 @@ bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int
 		// do decompress & draw
 		Res = jd_decomp(&Decoder, OutHandlerDirect, N);
 		if (Res == JDR_OK) {
+			Device->Dirty = true;
 			Ret = true;
 		} else {	
 			ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);

+ 5 - 4
components/display/core/gds_image.h

@@ -7,8 +7,9 @@
 
 struct GDS_Device;
 
-enum { GDS_RGB565, GDS_RGB555, GDS_RGB444 };
+enum { GDS_RGB565, GDS_RGB555, GDS_RGB444, GDS_RGB332, GDS_GRAYSCALE };
 
+// Fit options for GDS_DrawJPEG
 #define GDS_IMAGE_LEFT		0x00
 #define GDS_IMAGE_CENTER_X	0x01
 #define GDS_IMAGE_RIGHT		0x04
@@ -16,11 +17,11 @@ enum { GDS_RGB565, GDS_RGB555, GDS_RGB444 };
 #define GDS_IMAGE_BOTTOM	0x08
 #define GDS_IMAGE_CENTER_Y	0x02
 #define GDS_IMAGE_CENTER	(GDS_IMAGE_CENTER_X | GDS_IMAGE_CENTER_Y)
-#define GDS_IMAGE_FIT		0x10
+#define GDS_IMAGE_FIT		0x10	// re-scale by a factor of 2^N (up to 3)
 
 // Width and Height can be NULL if you already know them (actual scaling is closest ^2)
 uint16_t* 	GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale);
 void	 	GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height);
+bool 		GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit);
 void 		GDS_DrawRGB16( struct GDS_Device* Device, uint16_t *Image, int x, int y, int Width, int Height, int RGB_Mode );
-// set DisplayWidth and DisplayHeight to non-zero if you want autoscale to closest factor ^2 from 0..3
-bool 		GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int Fit);
+void 		GDS_DrawRGB8( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode );

+ 2 - 1
components/display/core/gds_private.h

@@ -111,7 +111,8 @@ struct GDS_Device {
 	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 );
 	// may provide for optimization
-	void (*DrawRGB16)( struct GDS_Device* Device, int x, int y, int Width, int Height, int RGB_Mode, uint16_t *Image );
+	void (*DrawRGB16)( struct GDS_Device* Device, uint16_t *Image,int x, int y, int Width, int Height, int RGB_Mode );
+	void (*DrawRGB8)( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode );
 	void (*ClearWindow)( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );
 		    
 	// interface-specific methods	

+ 4 - 3
components/raop/rtp.c

@@ -655,7 +655,7 @@ static void *rtp_thread_func(void *arg) {
 				u32_t remote_gap = NTP2MS(remote - ctx->timing.remote);
 
 				// try to get NTP every 3 sec or every time if we are not synced
-				if (!count-- || !(ctx->synchro.status && NTP_SYNC)) {
+				if (!count-- || !(ctx->synchro.status & NTP_SYNC)) {
 					rtp_request_timing(ctx);
 					count = 3;
 				}
@@ -701,9 +701,10 @@ static void *rtp_thread_func(void *arg) {
 				u64_t remote 	  =(((u64_t) ntohl(*(u32_t*)(pktp+16))) << 32) + ntohl(*(u32_t*)(pktp+20));
 				u32_t roundtrip   = gettime_ms() - reference;
 				
-				// better discard sync packets when roundtrip is suspicious and ask for another one
+				// better discard sync packets when roundtrip is suspicious
 				if (roundtrip > 100) {
-					rtp_request_timing(ctx);
+					// ask for another one only if we are not synced already
+					if (!(ctx->synchro.status & NTP_SYNC)) rtp_request_timing(ctx);
 					LOG_WARN("[%p]: discarding NTP roundtrip of %u ms", ctx, roundtrip);
 					break;
 				}

+ 1 - 0
components/squeezelite/CMakeLists.txt

@@ -12,6 +12,7 @@ idf_component_register( SRC_DIRS . external a1s tas57xx
 						 			services 
 						 			raop   
 						 			display
+						 EMBED_FILES vu.data
 )
 
 

+ 1 - 0
components/squeezelite/component.mk

@@ -21,4 +21,5 @@ CFLAGS += -O3 -DLINKALL -DLOOPBACK -DNO_FAAD -DRESAMPLE16 -DEMBEDDED -DTREMOR_ON
 
 COMPONENT_SRCDIRS := . tas57xx a1s external
 COMPONENT_ADD_INCLUDEDIRS := . ./tas57xx ./a1s
+COMPONENT_EMBED_FILES := vu.data
 

+ 99 - 40
components/squeezelite/display.c

@@ -73,9 +73,12 @@ struct visu_packet {
 	u8_t which;
 	u8_t count;
 	union {
-		struct {
-			u32_t bars;
-			u32_t spectrum_scale;
+		union {
+			struct {
+				u32_t bars;
+				u32_t spectrum_scale;
+			};
+			u32_t style;	
 		} full;	
 		struct {	
 			u32_t width;
@@ -137,6 +140,10 @@ static struct {
 #define RMS_LEN_BIT	6
 #define RMS_LEN		(1 << RMS_LEN_BIT)
 
+#define VU_WIDTH	160
+#define VU_HEIGHT	SB_HEIGHT
+#define VU_COUNT	48
+
 #define DISPLAY_BW	20000
 
 static struct scroller_s {
@@ -178,7 +185,7 @@ static EXT_RAM_ATTR struct {
 		int limit;
 	} bars[MAX_BARS];
 	float spectrum_scale;
-	int n, col, row, height, width, border;
+	int n, col, row, height, width, border, style, max;
 	enum { VISU_BLANK, VISU_VUMETER, VISU_SPECTRUM, VISU_WAVEFORM } mode;
 	int speed, wake;	
 	float fft[FFT_LEN*2], samples[FFT_LEN*2], hanning[FFT_LEN];
@@ -189,6 +196,8 @@ static EXT_RAM_ATTR struct {
 	} back;		
 } visu;
 
+extern const uint8_t vu_bitmap[]   asm("_binary_vu_data_start");
+
 #define ANIM_NONE		  0x00
 #define ANIM_TRANSITION   0x01 // A transition animation has finished
 #define ANIM_SCROLL_ONCE  0x02 
@@ -565,6 +574,35 @@ static void vfdc_handler( u8_t *_data, int bytes_read) {
 	show_display_buffer(ddram);
 }
 
+/****************************************************************************************
+ * Display VU-Meter (lots of hard-coding)
+ */
+void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x, int y, int width) {
+	// VU data is by columns and vertical flip to allow block offset 
+	data += level * VU_WIDTH * VU_HEIGHT;
+	
+	// adjust to current display window
+	if (width > VU_WIDTH) {
+		width = VU_WIDTH;
+		x += (width - VU_WIDTH) / 2;
+	} else {
+		data += (VU_WIDTH - width) / 2 * VU_HEIGHT;	
+	}	
+
+	// this is 8 bits grayscale
+	int scale = 8 - GDS_GetDepth(display);
+	
+	// use "fast" version as we are not beyond screen boundaries
+	for (int r = 0; r < width; r++) {
+		for (int c = 0; c < VU_HEIGHT; c++) {
+			GDS_DrawPixelFast(display, r + x, c + y, *data++ >> scale);
+		}	
+	}	
+	
+	// need to manually set dirty flag as DrawPixel does not do it
+	GDS_SetDirty(display);
+}
+
 /****************************************************************************************
  * Process graphic display data
  */
@@ -574,12 +612,13 @@ static void grfe_handler( u8_t *data, int len) {
 	
 	scroller.active = false;
 	
-	// we are not in control or we are displaying visu on a small screen, do not do screen update
+	// visu has priority when full screen on small screens
 	if ((visu.mode & VISU_ESP32) && !visu.col && visu.row < SB_HEIGHT) {
 		xSemaphoreGive(displayer.mutex);
 		return;
 	}	
 	
+	// are we in control
 	if (displayer.owned) {
 		// did we have something that might have write on the bottom of a SB_HEIGHT+ display
 		if (displayer.dirty) {
@@ -694,9 +733,12 @@ static void grfg_handler(u8_t *data, int len) {
 	struct grfg_packet *pkt = (struct grfg_packet*) data;
 	
 	LOG_DEBUG("gfrg s:%hu w:%hu (len:%u)", htons(pkt->screen), htons(pkt->width), len);
+
+	// on small screen, visu has priority when full screen	
+	if ((visu.mode & VISU_ESP32) && !visu.col && visu.row < SB_HEIGHT) return;
 	
 	xSemaphoreTake(displayer.mutex, portMAX_DELAY);
-	
+		
 	// size of scrollable area (less than background)
 	scroller.width = htons(pkt->width);
 	scroller.back.width = ((len - sizeof(struct grfg_packet)) * 8) / displayer.height;
@@ -733,14 +775,21 @@ static void grfa_handler(u8_t *data, int len) {
 	int size = len - sizeof(struct grfa_packet);
 	int offset = htonl(pkt->offset);
 	int length = htonl(pkt->length);
-	
+
+	// when using full screen visualizer on small screen there is a brief overlay	
 	artwork.enable = (length != 0);
-	
-	// clean up if we are disabling previously enabled artwork
-	if (!artwork.enable) {
-		if (artwork.size) GDS_ClearWindow(display, artwork.x, artwork.y, -1, -1, GDS_COLOR_BLACK);
+
+	// just a config or an actual artwork	
+	if (length < 32) {
+		if (artwork.enable) {
+			// this is just to specify artwork coordinates
+			artwork.x = htons(pkt->x);
+			artwork.y = htons(pkt->y);		
+		} else if (artwork.size) GDS_ClearWindow(display, artwork.x, artwork.y, -1, -1, GDS_COLOR_BLACK);
+		
+		// done in any case
 		return;
-	}	
+	}
 	
 	// new grfa artwork, allocate memory
 	if (!offset) {	
@@ -804,7 +853,7 @@ static void visu_update(void) {
 			// convert to dB (1 bit remaining for getting X²/N, 60dB dynamic starting from 0dBFS = 3 bits back-off)
 			for (int i = visu.n; --i >= 0;) {	 
 				visu.bars[i].current = SB_HEIGHT * (0.01667f*10*log10f(0.0000001f + (visu.bars[i].current >> 1)) - 0.2543f);
-				if (visu.bars[i].current > 31) visu.bars[i].current = 31;
+				if (visu.bars[i].current > visu.max) visu.bars[i].current = visu.max;
 				else if (visu.bars[i].current < 0) visu.bars[i].current = 0;
 			}
 		} else {
@@ -846,7 +895,7 @@ static void visu_update(void) {
 			
 				// convert to dB and bars, same back-off
 				if (power) visu.bars[i].current = SB_HEIGHT * (0.01667f*10*(log10f(power) - log10f(FFT_LEN/2*2)) - 0.2543f);
-				if (visu.bars[i].current > 31) visu.bars[i].current = 31;
+				if (visu.bars[i].current > visu.max) visu.bars[i].current = visu.max;
 				else if (visu.bars[i].current < 0) visu.bars[i].current = 0;
 			}	
 		}
@@ -866,23 +915,31 @@ static void visu_update(void) {
 		GDS_DrawBitmapCBR(display, visu.back.frame, visu.back.width, displayer.height, GDS_COLOR_WHITE);
 	}	
 
-	// there is much more optimization to be done here, like not redrawing bars unless needed
-	for (int i = visu.n; --i >= 0;) {
-		int x1 = visu.col + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap);
-		int y1 = visu.row + visu.height - 1;
+	if (mode != VISU_VUMETER || !visu.style) {
+		// there is much more optimization to be done here, like not redrawing bars unless needed
+		for (int i = visu.n; --i >= 0;) {
+			int x1 = visu.col + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap);
+			int y1 = visu.row + visu.height - 1;
 			
-		if (visu.bars[i].current > visu.bars[i].max) visu.bars[i].max = visu.bars[i].current;
-		else if (visu.bars[i].max) visu.bars[i].max--;
-		else if (!clear) continue;
-		
-		for (int j = 0; j <= visu.bars[i].current; j += 2) 
-			GDS_DrawLine(display, x1, y1 - j, x1 + visu.bar_width - 1, y1 - j, GDS_COLOR_WHITE);
+			if (visu.bars[i].current > visu.bars[i].max) visu.bars[i].max = visu.bars[i].current;
+			else if (visu.bars[i].max) visu.bars[i].max--;
+			else if (!clear) continue;
 			
-		if (visu.bars[i].max > 2) {
-			GDS_DrawLine(display, x1, y1 - visu.bars[i].max, x1 + visu.bar_width - 1, y1 - visu.bars[i].max, GDS_COLOR_WHITE);			
-			GDS_DrawLine(display, x1, y1 - visu.bars[i].max + 1, x1 + visu.bar_width - 1, y1 - visu.bars[i].max + 1, GDS_COLOR_WHITE);			
-		}	
-	}
+			for (int j = 0; j <= visu.bars[i].current; j += 2) 
+				GDS_DrawLine(display, x1, y1 - j, x1 + visu.bar_width - 1, y1 - j, GDS_COLOR_WHITE);
+			
+			if (visu.bars[i].max > 2) {
+				GDS_DrawLine(display, x1, y1 - visu.bars[i].max, x1 + visu.bar_width - 1, y1 - visu.bars[i].max, GDS_COLOR_WHITE);			
+				GDS_DrawLine(display, x1, y1 - visu.bars[i].max + 1, x1 + visu.bar_width - 1, y1 - visu.bars[i].max + 1, GDS_COLOR_WHITE);			
+			}	
+		}
+	} else if (displayer.width / 2 >  3 * VU_WIDTH / 4) {
+		draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, displayer.width / 2);
+		draw_VU(display, vu_bitmap, visu.bars[1].current, displayer.width / 2, visu.row, displayer.width / 2);
+	} else {
+		int level = (visu.bars[0].current + visu.bars[1].current) / 2;
+		draw_VU(display, vu_bitmap, level, 0, visu.row, displayer.width);		
+	}	
 }
 
 
@@ -934,6 +991,7 @@ static void visu_handler( u8_t *data, int len) {
 				pkt->row = htonl(pkt->row);
 				pkt->col = htonl(pkt->col);
 
+				visu.style = 0;
 				visu.width = htonl(pkt->width);
 				visu.height = pkt->height ? pkt->height : SB_HEIGHT;
 				visu.col = pkt->col < 0 ? displayer.width + pkt->col : pkt->col;
@@ -944,27 +1002,36 @@ static void visu_handler( u8_t *data, int len) {
 			} else {
 				// full screen visu, try to use bottom screen if available
 				visu.height = GDS_GetHeight(display) > SB_HEIGHT ? GDS_GetHeight(display) - SB_HEIGHT : GDS_GetHeight(display);
-				bars = htonl(pkt->full.bars);
-				visu.spectrum_scale = htonl(pkt->full.spectrum_scale) / 100.;
 				visu.row = GDS_GetHeight(display) - visu.height;			
+				
+				// is this spectrum or analogue/digital
+				if ((visu.mode & ~VISU_ESP32) == VISU_SPECTRUM) {
+					bars = htonl(pkt->full.bars);
+					visu.spectrum_scale = htonl(pkt->full.spectrum_scale) / 100.;
+				} else {
+					// select analogue/digital style
+					visu.style = htonl(pkt->full.style);
+				}
 			}	
 		} else {
 			// classical (screensaver) mode, don't try to optimize screen usage & force some params
 			visu.row = 0;
 			visu.height = SB_HEIGHT;
 			visu.spectrum_scale = 0.25;				
-			if (artwork.enable && artwork.y < SB_HEIGHT) visu.width = artwork.x - 1;
 			if (visu.mode == VISU_SPECTRUM) bars = visu.width / (htonl(pkt->channels[0].bar_width) + htonl(pkt->channels[0].bar_space));
+			else visu.style = htonl(pkt->classical_vu.style);
 			if (bars > MAX_BARS) bars = MAX_BARS;
 		}	
 		
 		// try to adapt to what we have
 		if ((visu.mode & ~VISU_ESP32) == VISU_SPECTRUM) {
 			visu.n = bars ? bars : MAX_BARS;
+			visu.max = displayer.height - 1;
 			if (visu.spectrum_scale <= 0 || visu.spectrum_scale > 0.5) visu.spectrum_scale = 0.5;
 			spectrum_limits(0, visu.n, 0);
 		} else {
 			visu.n = 2;
+			visu.max = visu.style ? (VU_COUNT - 1) : (displayer.height - 1);
 		}	
 		
 		do {
@@ -1071,11 +1138,3 @@ static void displayer_task(void *args) {
 		visu.wake -= sleep;
 	}	
 }	
-			
-
-
-
-
-
-
-

BIN
components/squeezelite/vu.data


BIN
plugin/SqueezeESP32.zip


+ 30 - 10
plugin/SqueezeESP32/Graphics.pm

@@ -42,7 +42,10 @@ sub new {
 		
 	$display->init_accessor(	
 		modes => $display->build_modes,
-		vfdmodel => 'graphic-<width>x32',	# doesn't matter much
+		# Only seems to matter for screensaver and update to decide font. Not 
+		# any value is acceptable, so use Boom value which seems to be best 
+		# compromise
+		vfdmodel => 'graphic-160x32',	
 	);	
 	
 	return $display;
@@ -165,32 +168,49 @@ sub build_modes {
 		# mode 9	 
 		{ desc => ['VISUALIZER_VUMETER'],
 		bar => 0, secs => 0,  width => $width,
-		params => [$VISUALIZER_VUMETER_ESP32] },
-		# mode 10
+		params => [$VISUALIZER_VUMETER_ESP32, 0] },
+		# mode 10	
+		{ desc => ['VISUALIZER_ANALOG_VUMETER'],
+		bar => 0, secs => 0,  width => $width,
+		params => [$VISUALIZER_VUMETER_ESP32, 1] },
+		# mode 11
 		{ desc => ['VISUALIZER_SPECTRUM_ANALYZER'],
 		bar => 0, secs => 0,  width => $width,
 		# extra parameters (bars)
 		params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },	  
-		# mode 11	 
+	);
+
+my @extra = (
+		# mode E1
 		{ desc => ['VISUALIZER_VUMETER', 'AND', 'ELAPSED'],
 		bar => 0, secs => 1,  width => $width,
-		params => [$VISUALIZER_VUMETER_ESP32] },
-		# mode 12
+		params => [$VISUALIZER_VUMETER_ESP32, 0] },
+		# mode E2	 
+		{ desc => ['VISUALIZER_ANALOG_VUMETER', 'AND', 'ELAPSED'],
+		bar => 0, secs => 1,  width => $width,
+		params => [$VISUALIZER_VUMETER_ESP32, 1] },
+		# mode E3
 		{ desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'ELAPSED'],
 		bar => 0, secs => 1,  width => $width,
 		# extra parameters (bars)
 		params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },	  
-		# mode 13	 
+		# mode E4	 
 		{ desc => ['VISUALIZER_VUMETER', 'AND', 'REMAINING'],
 		bar => 0, secs => -1,  width => $width,
-		params => [$VISUALIZER_VUMETER_ESP32] },
-		# mode 14
+		params => [$VISUALIZER_VUMETER_ESP32, 0] },
+		# mode E5
+		{ desc => ['VISUALIZER_ANALOG_VUMETER', 'AND', 'REMAINING'],
+		bar => 0, secs => -1,  width => $width,
+		params => [$VISUALIZER_VUMETER_ESP32, 1] },
+		# mode E6
 		{ desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'REMAINING'],
 		bar => 0, secs => -1,  width => $width,
 		# extra parameters (bars)
 		params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },	
-	);
+	);		
 	
+	@modes = (@modes, @extra) if $cprefs->get('height') > 32;
+		
 	return \@modes;
 }	
 

+ 12 - 3
plugin/SqueezeESP32/Player.pm

@@ -13,6 +13,12 @@ sub model { 'squeezeesp32' }
 sub modelName { 'SqueezeESP32' }
 sub hasIR { 0 }
 
+sub init {
+	my $client = shift;
+	$client->SUPER::init(@_);
+	Plugins::SqueezeESP32::Plugin::config_artwork($client);
+}
+
 # Allow the player to define it's display width (and probably more)
 sub playerSettingsFrame {
 	my $client   = shift;
@@ -26,13 +32,16 @@ sub playerSettingsFrame {
 		$value = (unpack('Cn', $$data_ref))[1];
 		if ($value > 100 && $value < 400) {
 			$prefs->client($client)->set('width', $value);
+			
+			my $height = (unpack('Cnn', $$data_ref))[2];
+			$prefs->client($client)->set('height', $height || 0);
+
 			$client->display->modes($client->display->build_modes);
 			$client->display->widthOverride(1, $value);
 			$client->update;
+			
+			$log->info("Setting player $value" . "x" . "$height for ", $client->name);
 		} 
-		my $height = (unpack('Cnn', $$data_ref))[2];
-		$prefs->client($client)->set('height', $height || 0);
-		$log->info("Setting player $value" . "x" . "$height for ", $client->name);
 	}
 	
 	$client->SUPER::playerSettingsFrame($data_ref);

+ 1 - 1
plugin/SqueezeESP32/PlayerSettings.pm

@@ -59,7 +59,7 @@ sub handler {
 		if ($artwork->{'enable'}) {
 			Plugins::SqueezeESP32::Plugin::update_artwork($client, 1);
 		} else {
-			Plugins::SqueezeESP32::Plugin::disable_artwork($client);
+			Plugins::SqueezeESP32::Plugin::config_artwork($client);
 		}	
 	}
 	

+ 7 - 6
plugin/SqueezeESP32/Plugin.pm

@@ -50,7 +50,7 @@ sub onNotification {
 
 sub update_artwork {
     my $client  = shift;
-	my $force = shift || 0;
+	my $params = { force => shift || 0 };
 	my $cprefs = $prefs->client($client);
 	my $artwork = $cprefs->get('artwork');
 		
@@ -60,17 +60,17 @@ sub update_artwork {
 	$s = min($s, $cprefs->get('width') - $artwork->{'x'});
 	
 	my $path = 'music/current/cover_' . $s . 'x' . $s . '_o.jpg';
-	my $body = Slim::Web::Graphics::artworkRequest($client, $path, $force, \&send_artwork, undef, HTTP::Response->new);
+	my $body = Slim::Web::Graphics::artworkRequest($client, $path, $params, \&send_artwork, undef, HTTP::Response->new);
 	
 	send_artwork($client, undef, \$body) if $body;
 }
 
 sub send_artwork {
-	my ($client, $force, $dataref) = @_;
+	my ($client, $params, $dataref) = @_;
 	
 	# I'm not sure why we are called so often, so only send when needed
 	my $md5 = md5($$dataref);
-	return if $client->pluginData('artwork_md5') eq $md5 && !$force;
+	return if $client->pluginData('artwork_md5') eq $md5 && !$params->{'force'};
 	
 	$client->pluginData('artwork', $dataref);
 	$client->pluginData('artwork_md5', $md5);
@@ -95,9 +95,10 @@ sub send_artwork {
 	}
 }	
 
-sub disable_artwork {
+sub config_artwork {
 	my ($client) = @_;
-	my $header = pack('N', 0);
+	my $artwork = $prefs->client($client)->get('artwork');
+	my $header = pack('Nnn', $artwork->{'enable'}, $artwork->{'x'}, $artwork->{'y'});
 	$client->sendFrame( grfa => \$header );
 }
 

+ 1 - 1
plugin/SqueezeESP32/install.xml

@@ -10,6 +10,6 @@
   <name>PLUGIN_SQUEEZEESP32</name>
   <description>PLUGIN_SQUEEZEESP32_DESC</description>
   <module>Plugins::SqueezeESP32::Plugin</module>
-    <version>0.50</version>
+    <version>0.61</version>
   <creator>Philippe</creator>
 </extensions>

+ 2 - 2
plugin/repo.xml

@@ -1,10 +1,10 @@
 <?xml version='1.0' standalone='yes'?>
 <extensions>
   <plugins>
-    <plugin version="0.50" name="SqueezeESP32" minTarget="7.5" maxTarget="*">
+    <plugin version="0.61" name="SqueezeESP32" minTarget="7.5" maxTarget="*">
       <link>https://github.com/sle118/squeezelite-esp32</link>
       <creator>Philippe</creator>
-      <sha>47feaf69a40ad4f87c58b34212d71e60dca99d3e</sha>
+      <sha>5c45fed832e6f79f709bef5f2da511071d1c776e</sha>
       <email>philippe_44@outlook.com</email>
       <desc lang="EN">SqueezeESP32 additional player id (100)</desc>
       <url>http://github.com/sle118/squeezelite-esp32/raw/master/plugin/SqueezeESP32.zip</url>