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

Conflicts:
	components/raop/rtp.c
This commit is contained in:
Sebastien
2020-03-31 22:43:24 -04:00
18 changed files with 270 additions and 88 deletions

View File

@@ -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>". - '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 ### Set GPIO
The parameter "set_GPIO" is use to assign GPIO to various functions. The parameter "set_GPIO" is use to assign GPIO to various functions.

View File

@@ -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_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_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_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_GetWidth( struct GDS_Device* Device ) { return Device->Width; }
int GDS_GetHeight( struct GDS_Device* Device ) { return Device->Height; } 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_DisplayOn( struct GDS_Device* Device ) { if (Device->DisplayOn) Device->DisplayOn( Device ); }
void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); } void GDS_DisplayOff( struct GDS_Device* Device ) { if (Device->DisplayOff) Device->DisplayOff( Device ); }

View File

@@ -35,8 +35,10 @@ void GDS_DisplayOff( struct GDS_Device* Device );
void GDS_Update( struct GDS_Device* Device ); void GDS_Update( struct GDS_Device* Device );
void GDS_SetHFlip( struct GDS_Device* Device, bool On ); void GDS_SetHFlip( struct GDS_Device* Device, bool On );
void GDS_SetVFlip( 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_GetWidth( struct GDS_Device* Device );
int GDS_GetHeight( 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_ClearExt( struct GDS_Device* Device, bool full, ...);
void GDS_Clear( struct GDS_Device* Device, int Color ); 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 ); void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );

View File

@@ -133,45 +133,126 @@ void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height) {
} }
/**************************************************************************************** /****************************************************************************************
* Simply draw a RGB565 image * Simply draw a RGB 16bits image
* monoschrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b) * monochrome (0.2125 * color.r) + (0.7154 * color.g) + (0.0721 * color.b)
* grayscale (0.3 * R) + (0.59 * G) + (0.11 * 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 ) { void GDS_DrawRGB16( struct GDS_Device* Device, uint16_t *Image, int x, int y, int Width, int Height, int RGB_Mode ) {
if (Device->DrawRGB16) { if (Device->DrawRGB16) {
Device->DrawRGB16( Device, x, y, Width, Height, RGB_Mode, Image ); Device->DrawRGB16( Device, Image, x, y, Width, Height, RGB_Mode );
} else { } else {
int Scale = Device->Depth < 5 ? 5 - Device->Depth : 0;
switch(RGB_Mode) { switch(RGB_Mode) {
case GDS_RGB565: 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 r = 0; r < Height; r++) {
int pixel = Image[Width*r + c]; for (int c = 0; c < Width; c++) {
pixel = ((pixel & 0x1f) * 11 + ((((pixel >> 5) & 0x3f) * 59) >> 1) + (pixel >> 11) * 30) / 100; 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); GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale);
} }
} }
} else {
int Scale = Device->Depth - 6;
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);
}
}
}
break; break;
case GDS_RGB555: 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++) { for (int r = 0; r < Height; r++) {
int pixel = Image[Width*r + c]; for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100; pixel = ((pixel & 0x1f) * 11 + ((pixel >> 5) & 0x1f) * 59 + (pixel >> 10) * 30) / 100;
GDS_DrawPixel( Device, c + x, r + y, pixel >> Scale); 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; break;
case GDS_RGB444: 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++) { for (int r = 0; r < Height; r++) {
int pixel = Image[Width*r + c]; for (int c = 0; c < Width; c++) {
int pixel = *Image++;
pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30; pixel = (pixel & 0x0f) * 11 + ((pixel >> 4) & 0x0f) * 59 + (pixel >> 8) * 30;
GDS_DrawPixel( Device, c + x, r + y, pixel >> (Scale - 1)); 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; 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. //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 // do decompress & draw
Res = jd_decomp(&Decoder, OutHandlerDirect, N); Res = jd_decomp(&Decoder, OutHandlerDirect, N);
if (Res == JDR_OK) { if (Res == JDR_OK) {
Device->Dirty = true;
Ret = true; Ret = true;
} else { } else {
ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res); ESP_LOGE(TAG, "Image decoder: jd_decode failed (%d)", Res);

View File

@@ -7,8 +7,9 @@
struct GDS_Device; 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_LEFT 0x00
#define GDS_IMAGE_CENTER_X 0x01 #define GDS_IMAGE_CENTER_X 0x01
#define GDS_IMAGE_RIGHT 0x04 #define GDS_IMAGE_RIGHT 0x04
@@ -16,11 +17,11 @@ enum { GDS_RGB565, GDS_RGB555, GDS_RGB444 };
#define GDS_IMAGE_BOTTOM 0x08 #define GDS_IMAGE_BOTTOM 0x08
#define GDS_IMAGE_CENTER_Y 0x02 #define GDS_IMAGE_CENTER_Y 0x02
#define GDS_IMAGE_CENTER (GDS_IMAGE_CENTER_X | GDS_IMAGE_CENTER_Y) #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) // 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); uint16_t* GDS_DecodeJPEG(uint8_t *Source, int *Width, int *Height, float Scale);
void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height); void GDS_GetJPEGSize(uint8_t *Source, int *Width, int *Height);
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); 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 );
void GDS_DrawRGB8( struct GDS_Device* Device, uint8_t *Image, int x, int y, int Width, int Height, int RGB_Mode );

View File

@@ -111,7 +111,8 @@ struct GDS_Device {
void (*DrawPixelFast)( struct GDS_Device* Device, int X, int Y, int Color ); 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 ); void (*DrawBitmapCBR)(struct GDS_Device* Device, uint8_t *Data, int Width, int Height, int Color );
// may provide for optimization // 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 ); void (*ClearWindow)( struct GDS_Device* Device, int x1, int y1, int x2, int y2, int Color );
// interface-specific methods // interface-specific methods

View File

@@ -655,7 +655,7 @@ static void *rtp_thread_func(void *arg) {
u64_t remote = (((u64_t) ntohl(*(u32_t*)(pktp+8))) << 32) + ntohl(*(u32_t*)(pktp+12)); u64_t remote = (((u64_t) ntohl(*(u32_t*)(pktp+8))) << 32) + ntohl(*(u32_t*)(pktp+12));
u32_t rtp_now = ntohl(*(u32_t*)(pktp+16)); u32_t rtp_now = ntohl(*(u32_t*)(pktp+16));
u16_t flags = ntohs(*(u16_t*)(pktp+2)); u16_t flags = ntohs(*(u16_t*)(pktp+2));
u32_t remote_gap = NTP2MS(remote - ctx->timing.remote); u32_t remote_gap = NTP2MS(remote - ctx->timing.remote);
// try to get NTP every 3 sec or every time if we are not synced // 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)) {
@@ -701,9 +701,10 @@ static void *rtp_thread_func(void *arg) {
case 0x53: { case 0x53: {
u64_t expected; u64_t expected;
u32_t reference = ntohl(*(u32_t*)(pktp+12)); // only low 32 bits in our case u32_t reference = ntohl(*(u32_t*)(pktp+12)); // only low 32 bits in our case
u64_t remote =(((u64_t) ntohl(*(u32_t*)(pktp+16))) << 32) + ntohl(*(u32_t*)(pktp+20)); u64_t remote =(((u64_t) ntohl(*(u32_t*)(pktp+16))) << 32) + ntohl(*(u32_t*)(pktp+20));
u32_t roundtrip = gettime_ms() - reference; u32_t roundtrip = gettime_ms() - reference;
// better discard sync packets when roundtrip is suspicious
if (roundtrip > 100) { if (roundtrip > 100) {
// ask for another one only if we are not synced already // ask for another one only if we are not synced already
if (!(ctx->synchro.status & NTP_SYNC)) rtp_request_timing(ctx); if (!(ctx->synchro.status & NTP_SYNC)) rtp_request_timing(ctx);

View File

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

View File

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

View File

@@ -72,10 +72,13 @@ struct visu_packet {
char opcode[4]; char opcode[4];
u8_t which; u8_t which;
u8_t count; u8_t count;
union {
union { union {
struct { struct {
u32_t bars; u32_t bars;
u32_t spectrum_scale; u32_t spectrum_scale;
};
u32_t style;
} full; } full;
struct { struct {
u32_t width; u32_t width;
@@ -137,6 +140,10 @@ static struct {
#define RMS_LEN_BIT 6 #define RMS_LEN_BIT 6
#define RMS_LEN (1 << RMS_LEN_BIT) #define RMS_LEN (1 << RMS_LEN_BIT)
#define VU_WIDTH 160
#define VU_HEIGHT SB_HEIGHT
#define VU_COUNT 48
#define DISPLAY_BW 20000 #define DISPLAY_BW 20000
static struct scroller_s { static struct scroller_s {
@@ -178,7 +185,7 @@ static EXT_RAM_ATTR struct {
int limit; int limit;
} bars[MAX_BARS]; } bars[MAX_BARS];
float spectrum_scale; 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; enum { VISU_BLANK, VISU_VUMETER, VISU_SPECTRUM, VISU_WAVEFORM } mode;
int speed, wake; int speed, wake;
float fft[FFT_LEN*2], samples[FFT_LEN*2], hanning[FFT_LEN]; float fft[FFT_LEN*2], samples[FFT_LEN*2], hanning[FFT_LEN];
@@ -189,6 +196,8 @@ static EXT_RAM_ATTR struct {
} back; } back;
} visu; } visu;
extern const uint8_t vu_bitmap[] asm("_binary_vu_data_start");
#define ANIM_NONE 0x00 #define ANIM_NONE 0x00
#define ANIM_TRANSITION 0x01 // A transition animation has finished #define ANIM_TRANSITION 0x01 // A transition animation has finished
#define ANIM_SCROLL_ONCE 0x02 #define ANIM_SCROLL_ONCE 0x02
@@ -565,6 +574,35 @@ static void vfdc_handler( u8_t *_data, int bytes_read) {
show_display_buffer(ddram); 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 * Process graphic display data
*/ */
@@ -574,12 +612,13 @@ static void grfe_handler( u8_t *data, int len) {
scroller.active = false; 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) { if ((visu.mode & VISU_ESP32) && !visu.col && visu.row < SB_HEIGHT) {
xSemaphoreGive(displayer.mutex); xSemaphoreGive(displayer.mutex);
return; return;
} }
// are we in control
if (displayer.owned) { if (displayer.owned) {
// did we have something that might have write on the bottom of a SB_HEIGHT+ display // did we have something that might have write on the bottom of a SB_HEIGHT+ display
if (displayer.dirty) { if (displayer.dirty) {
@@ -695,6 +734,9 @@ static void grfg_handler(u8_t *data, int len) {
LOG_DEBUG("gfrg s:%hu w:%hu (len:%u)", htons(pkt->screen), htons(pkt->width), len); 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); xSemaphoreTake(displayer.mutex, portMAX_DELAY);
// size of scrollable area (less than background) // size of scrollable area (less than background)
@@ -734,11 +776,18 @@ static void grfa_handler(u8_t *data, int len) {
int offset = htonl(pkt->offset); int offset = htonl(pkt->offset);
int length = htonl(pkt->length); int length = htonl(pkt->length);
// when using full screen visualizer on small screen there is a brief overlay
artwork.enable = (length != 0); artwork.enable = (length != 0);
// clean up if we are disabling previously enabled artwork // just a config or an actual artwork
if (!artwork.enable) { if (length < 32) {
if (artwork.size) GDS_ClearWindow(display, artwork.x, artwork.y, -1, -1, GDS_COLOR_BLACK); 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; return;
} }
@@ -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) // 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;) { 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); 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 if (visu.bars[i].current < 0) visu.bars[i].current = 0;
} }
} else { } else {
@@ -846,7 +895,7 @@ static void visu_update(void) {
// convert to dB and bars, same back-off // 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 (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; else if (visu.bars[i].current < 0) visu.bars[i].current = 0;
} }
} }
@@ -866,6 +915,7 @@ static void visu_update(void) {
GDS_DrawBitmapCBR(display, visu.back.frame, visu.back.width, displayer.height, GDS_COLOR_WHITE); GDS_DrawBitmapCBR(display, visu.back.frame, visu.back.width, displayer.height, GDS_COLOR_WHITE);
} }
if (mode != VISU_VUMETER || !visu.style) {
// there is much more optimization to be done here, like not redrawing bars unless needed // there is much more optimization to be done here, like not redrawing bars unless needed
for (int i = visu.n; --i >= 0;) { for (int i = visu.n; --i >= 0;) {
int x1 = visu.col + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap); int x1 = visu.col + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap);
@@ -883,6 +933,13 @@ static void visu_update(void) {
GDS_DrawLine(display, x1, y1 - visu.bars[i].max + 1, x1 + visu.bar_width - 1, y1 - visu.bars[i].max + 1, 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->row = htonl(pkt->row);
pkt->col = htonl(pkt->col); pkt->col = htonl(pkt->col);
visu.style = 0;
visu.width = htonl(pkt->width); visu.width = htonl(pkt->width);
visu.height = pkt->height ? pkt->height : SB_HEIGHT; visu.height = pkt->height ? pkt->height : SB_HEIGHT;
visu.col = pkt->col < 0 ? displayer.width + pkt->col : pkt->col; 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 { } else {
// full screen visu, try to use bottom screen if available // 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); visu.height = GDS_GetHeight(display) > SB_HEIGHT ? GDS_GetHeight(display) - SB_HEIGHT : GDS_GetHeight(display);
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); bars = htonl(pkt->full.bars);
visu.spectrum_scale = htonl(pkt->full.spectrum_scale) / 100.; visu.spectrum_scale = htonl(pkt->full.spectrum_scale) / 100.;
visu.row = GDS_GetHeight(display) - visu.height; } else {
// select analogue/digital style
visu.style = htonl(pkt->full.style);
}
} }
} else { } else {
// classical (screensaver) mode, don't try to optimize screen usage & force some params // classical (screensaver) mode, don't try to optimize screen usage & force some params
visu.row = 0; visu.row = 0;
visu.height = SB_HEIGHT; visu.height = SB_HEIGHT;
visu.spectrum_scale = 0.25; 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)); 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; if (bars > MAX_BARS) bars = MAX_BARS;
} }
// try to adapt to what we have // try to adapt to what we have
if ((visu.mode & ~VISU_ESP32) == VISU_SPECTRUM) { if ((visu.mode & ~VISU_ESP32) == VISU_SPECTRUM) {
visu.n = bars ? bars : MAX_BARS; 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; if (visu.spectrum_scale <= 0 || visu.spectrum_scale > 0.5) visu.spectrum_scale = 0.5;
spectrum_limits(0, visu.n, 0); spectrum_limits(0, visu.n, 0);
} else { } else {
visu.n = 2; visu.n = 2;
visu.max = visu.style ? (VU_COUNT - 1) : (displayer.height - 1);
} }
do { do {
@@ -1071,11 +1138,3 @@ static void displayer_task(void *args) {
visu.wake -= sleep; visu.wake -= sleep;
} }
} }

Binary file not shown.

Binary file not shown.

View File

@@ -42,7 +42,10 @@ sub new {
$display->init_accessor( $display->init_accessor(
modes => $display->build_modes, 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; return $display;
@@ -165,32 +168,49 @@ sub build_modes {
# mode 9 # mode 9
{ desc => ['VISUALIZER_VUMETER'], { desc => ['VISUALIZER_VUMETER'],
bar => 0, secs => 0, width => $width, bar => 0, secs => 0, width => $width,
params => [$VISUALIZER_VUMETER_ESP32] }, params => [$VISUALIZER_VUMETER_ESP32, 0] },
# mode 10 # mode 10
{ desc => ['VISUALIZER_ANALOG_VUMETER'],
bar => 0, secs => 0, width => $width,
params => [$VISUALIZER_VUMETER_ESP32, 1] },
# mode 11
{ desc => ['VISUALIZER_SPECTRUM_ANALYZER'], { desc => ['VISUALIZER_SPECTRUM_ANALYZER'],
bar => 0, secs => 0, width => $width, bar => 0, secs => 0, width => $width,
# extra parameters (bars) # extra parameters (bars)
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] }, params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
# mode 11 );
my @extra = (
# mode E1
{ desc => ['VISUALIZER_VUMETER', 'AND', 'ELAPSED'], { desc => ['VISUALIZER_VUMETER', 'AND', 'ELAPSED'],
bar => 0, secs => 1, width => $width, bar => 0, secs => 1, width => $width,
params => [$VISUALIZER_VUMETER_ESP32] }, params => [$VISUALIZER_VUMETER_ESP32, 0] },
# mode 12 # 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'], { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'ELAPSED'],
bar => 0, secs => 1, width => $width, bar => 0, secs => 1, width => $width,
# extra parameters (bars) # extra parameters (bars)
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] }, params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
# mode 13 # mode E4
{ desc => ['VISUALIZER_VUMETER', 'AND', 'REMAINING'], { desc => ['VISUALIZER_VUMETER', 'AND', 'REMAINING'],
bar => 0, secs => -1, width => $width, bar => 0, secs => -1, width => $width,
params => [$VISUALIZER_VUMETER_ESP32] }, params => [$VISUALIZER_VUMETER_ESP32, 0] },
# mode 14 # 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'], { desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'REMAINING'],
bar => 0, secs => -1, width => $width, bar => 0, secs => -1, width => $width,
# extra parameters (bars) # extra parameters (bars)
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] }, params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
); );
@modes = (@modes, @extra) if $cprefs->get('height') > 32;
return \@modes; return \@modes;
} }

View File

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

View File

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

View File

@@ -50,7 +50,7 @@ sub onNotification {
sub update_artwork { sub update_artwork {
my $client = shift; my $client = shift;
my $force = shift || 0; my $params = { force => shift || 0 };
my $cprefs = $prefs->client($client); my $cprefs = $prefs->client($client);
my $artwork = $cprefs->get('artwork'); my $artwork = $cprefs->get('artwork');
@@ -60,17 +60,17 @@ sub update_artwork {
$s = min($s, $cprefs->get('width') - $artwork->{'x'}); $s = min($s, $cprefs->get('width') - $artwork->{'x'});
my $path = 'music/current/cover_' . $s . 'x' . $s . '_o.jpg'; 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; send_artwork($client, undef, \$body) if $body;
} }
sub send_artwork { 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 # I'm not sure why we are called so often, so only send when needed
my $md5 = md5($$dataref); 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', $dataref);
$client->pluginData('artwork_md5', $md5); $client->pluginData('artwork_md5', $md5);
@@ -95,9 +95,10 @@ sub send_artwork {
} }
} }
sub disable_artwork { sub config_artwork {
my ($client) = @_; 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 ); $client->sendFrame( grfa => \$header );
} }

View File

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

View File

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