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

This commit is contained in:
Sebastien
2020-03-21 12:38:55 -04:00
19 changed files with 390 additions and 146 deletions

View File

@@ -74,7 +74,7 @@ static void Update( struct GDS_Device* Device ) {
CurrentPage = p + 1; CurrentPage = p + 1;
// actual write // 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 #else
@@ -115,10 +115,6 @@ static bool Init( struct GDS_Device* Device ) {
Device->WriteCommand( Device, 0x8D ); Device->WriteCommand( Device, 0x8D );
Device->WriteCommand( Device, 0x14 ); 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 // COM pins HW config (alternative:EN if 64, DIS if 32, remap:DIS) - some display might need something different
Device->WriteCommand( Device, 0xDA ); Device->WriteCommand( Device, 0xDA );
Device->WriteCommand( Device, ((Device->Height == 64 ? 1 : 0) << 4) | (0 < 5) ); Device->WriteCommand( Device, ((Device->Height == 64 ? 1 : 0) << 4) | (0 < 5) );
@@ -126,12 +122,6 @@ static bool Init( struct GDS_Device* Device ) {
// MUX Ratio // MUX Ratio
Device->WriteCommand( Device, 0xA8 ); Device->WriteCommand( Device, 0xA8 );
Device->WriteCommand( Device, Device->Height - 1); 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 // Display Offset
Device->WriteCommand( Device, 0xD3 ); Device->WriteCommand( Device, 0xD3 );
Device->WriteCommand( Device, 0 ); Device->WriteCommand( Device, 0 );
@@ -143,6 +133,9 @@ static bool Init( struct GDS_Device* Device ) {
Device->SetHFlip( Device, false ); Device->SetHFlip( Device, false );
// no Display Inversion // no Display Inversion
Device->WriteCommand( Device, 0xA6 ); Device->WriteCommand( Device, 0xA6 );
// set Clocks
Device->WriteCommand( Device, 0xD5 );
Device->WriteCommand( Device, ( 0x08 << 4 ) | 0x00 );
// set Adressing Mode Horizontal // set Adressing Mode Horizontal
Device->WriteCommand( Device, 0x20 ); Device->WriteCommand( Device, 0x20 );
Device->WriteCommand( Device, 0 ); Device->WriteCommand( Device, 0 );

View File

@@ -60,6 +60,10 @@ 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 ) {
// -1 means up to width/height
if (x2 < 0) x2 = Device->Width - 1;
if (y2 < 0) y2 = Device->Height - 1;
// driver can provide own optimized clear window // driver can provide own optimized clear window
if (Device->ClearWindow) { if (Device->ClearWindow) {
Device->ClearWindow( Device, x1, y1, x2, y2, Color ); Device->ClearWindow( Device, x1, y1, x2, y2, Color );

View File

@@ -212,6 +212,7 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
uint8_t *optr = Device->Framebuffer; uint8_t *optr = Device->Framebuffer;
int LineLen = Device->Width >> 1; int LineLen = Device->Width >> 1;
Height >>= 3; Height >>= 3;
Color &= 0x0f;
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) { for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
uint8_t Byte = BitReverseTable256[*Data++]; uint8_t Byte = BitReverseTable256[*Data++];
// we need to linearize code to let compiler better optimize // we need to linearize code to let compiler better optimize
@@ -223,7 +224,7 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1; *optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen;
} else { } else {
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
@@ -232,7 +233,7 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1; *optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen;
} }
// end of a column, move to next one // end of a column, move to next one
if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); } if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); }

View File

@@ -218,7 +218,9 @@ bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int
// then place it // then place it
if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = (Device->Width + x - Context.Width) / 2; if (Fit & GDS_IMAGE_CENTER_X) Context.XOfs = (Device->Width + x - Context.Width) / 2;
else if (Fit & GDS_IMAGE_RIGHT) Context.XOfs = Device->Width - Context.Width;
if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = (Device->Height + y - Context.Height) / 2; if (Fit & GDS_IMAGE_CENTER_Y) Context.YOfs = (Device->Height + y - Context.Height) / 2;
else if (Fit & GDS_IMAGE_BOTTOM) Context.YOfs = Device->Height - Context.Height;
Context.XMin = x - Context.XOfs; Context.XMin = x - Context.XOfs;
Context.YMin = y - Context.YOfs; Context.YMin = y - Context.YOfs;

View File

@@ -9,8 +9,11 @@ struct GDS_Device;
enum { GDS_RGB565, GDS_RGB555, GDS_RGB444 }; enum { GDS_RGB565, GDS_RGB555, GDS_RGB444 };
#define GDS_IMAGE_TOP 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_TOP 0x00
#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

View File

@@ -53,7 +53,7 @@ static raop_event_t raop_state;
static EXT_RAM_ATTR struct { static EXT_RAM_ATTR struct {
bool enabled; bool enabled;
int sum, count, win, errors[SYNC_WIN_RUN]; int sum, count, win, errors[SYNC_WIN_RUN];
u32_t len; s32_t len;
u32_t start_time, playtime; u32_t start_time, playtime;
} raop_sync; } raop_sync;
@@ -210,20 +210,21 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
// how many ms have we really played // how many ms have we really played
ms = now - output.updated + ((u64_t) (output.frames_played_dmp - output.device_frames) * 1000) / RAOP_SAMPLE_RATE; ms = now - output.updated + ((u64_t) (output.frames_played_dmp - output.device_frames) * 1000) / RAOP_SAMPLE_RATE;
error = ms - (now - raop_sync.start_time); error = ms - (now - raop_sync.start_time);
LOG_INFO("backend played %u, desired %u, (delta:%d)", ms, now - raop_sync.start_time, ms - (now - raop_sync.start_time)); LOG_INFO("backend played %u, desired %u, (delta:%d)", ms, now - raop_sync.start_time, ms - (now - raop_sync.start_time));
} else { } else {
u32_t level = _buf_used(outputbuf);
// in how many ms will the most recent block play // 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); ms = (((s32_t)(level - raop_sync.len) / BYTES_PER_FRAME + output.device_frames + output.frames_in_process) * 10) / (RAOP_SAMPLE_RATE / 100) - (s32_t) (now - output.updated);
error = (raop_sync.playtime - now) - ms;
// make sure impact of erroneous point is limited, but taken into account // when outputbuf is empty, it means we have a network black-out or something
if (abs(error) > 1000) { error = level ? (raop_sync.playtime - now) - ms : 0;
LOG_INFO("limiting erroneous sync point %d", error);
error = (error > 0) ? SYNC_WIN_RUN : -SYNC_WIN_RUN; if (loglevel == lDEBUG || !level) {
LOG_INFO("head local:%d, remote:%d (delta:%d)", ms, raop_sync.playtime - now, error);
LOG_INFO("obuf:%u, sync_len:%u, devframes:%u, inproc:%u", _buf_used(outputbuf), raop_sync.len, output.device_frames, output.frames_in_process);
} }
LOG_DEBUG("head local:%u, remote:%u (delta:%d)", ms, raop_sync.playtime - now, error);
LOG_DEBUG("obuf:%u, sync_len:%u, devframes:%u, inproc:%u", _buf_used(outputbuf), raop_sync.len, output.device_frames, output.frames_in_process);
} }
// calculate sum, error and update sliding window // calculate sum, error and update sliding window
@@ -232,7 +233,10 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
error = raop_sync.sum / min(raop_sync.count, raop_sync.win); error = raop_sync.sum / min(raop_sync.count, raop_sync.win);
// move to normal mode if possible // move to normal mode if possible
if (raop_sync.win == SYNC_WIN_START && raop_sync.count >= SYNC_WIN_START && abs(error) < 10) raop_sync.win = SYNC_WIN_RUN; if (raop_sync.win == SYNC_WIN_START && raop_sync.count >= SYNC_WIN_START && abs(error) < 10) {
raop_sync.win = SYNC_WIN_RUN;
LOG_INFO("switching to slow sync mode %u", raop_sync.win);
}
// wait till e have enough data or there is a strong deviation // wait till e have enough data or there is a strong deviation
if ((raop_sync.count >= raop_sync.win && abs(error) > 10) || (raop_sync.count >= SYNC_WIN_CHECK && abs(error) > 100)) { if ((raop_sync.count >= raop_sync.win && abs(error) > 10) || (raop_sync.count >= SYNC_WIN_CHECK && abs(error) > 100)) {
@@ -241,11 +245,11 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
if (error < 0) { if (error < 0) {
output.skip_frames = -(error * RAOP_SAMPLE_RATE) / 1000; output.skip_frames = -(error * RAOP_SAMPLE_RATE) / 1000;
output.state = OUTPUT_SKIP_FRAMES; output.state = OUTPUT_SKIP_FRAMES;
LOG_INFO("skipping %u frames", output.skip_frames); LOG_INFO("skipping %u frames (count:%d)", output.skip_frames, raop_sync.count);
} else { } else {
output.pause_frames = (error * RAOP_SAMPLE_RATE) / 1000; output.pause_frames = (error * RAOP_SAMPLE_RATE) / 1000;
output.state = OUTPUT_PAUSE_FRAMES; output.state = OUTPUT_PAUSE_FRAMES;
LOG_INFO("pausing for %u frames", output.pause_frames); LOG_INFO("pausing for %u frames (count: %d)", output.pause_frames, raop_sync.count);
} }
// reset sliding window // reset sliding window

View File

@@ -26,6 +26,7 @@
#include "gds.h" #include "gds.h"
#include "gds_text.h" #include "gds_text.h"
#include "gds_draw.h" #include "gds_draw.h"
#include "gds_image.h"
#pragma pack(push, 1) #pragma pack(push, 1)
@@ -59,6 +60,14 @@ struct grfg_packet {
u16_t width; // # of pixels of scrollable u16_t width; // # of pixels of scrollable
}; };
struct grfa_packet {
char opcode[4];
u32_t length;
u16_t x;
u16_t y;
u32_t offset;
};
struct visu_packet { struct visu_packet {
char opcode[4]; char opcode[4];
u8_t which; u8_t which;
@@ -77,6 +86,29 @@ struct visu_packet {
u32_t bars; u32_t bars;
u32_t spectrum_scale; u32_t spectrum_scale;
}; };
struct {
u32_t mono;
u32_t bandwidth;
u32_t preemph;
struct {
u32_t pos;
u32_t width;
u32_t orient;
u32_t bar_width;
u32_t bar_space;
u32_t clipping;
u32_t bar_intens;
u32_t bar_cap_intens;
} channels[2];
};
struct {
u32_t mono;
u32_t style;
struct {
u32_t pos;
u32_t width;
} channels[2];
} classical_vu;
}; };
}; };
@@ -130,7 +162,15 @@ static struct scroller_s {
u32_t width; u32_t width;
} scroller; } scroller;
static struct {
u8_t *data;
u32_t size;
u16_t x, y;
bool enable;
} artwork;
#define MAX_BARS 32 #define MAX_BARS 32
#define VISU_ESP32 0x10
static EXT_RAM_ATTR struct { static EXT_RAM_ATTR struct {
int bar_gap, bar_width, bar_border; int bar_gap, bar_width, bar_border;
struct { struct {
@@ -142,6 +182,11 @@ static EXT_RAM_ATTR struct {
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];
struct {
u8_t *frame;
int width;
bool active;
} back;
} visu; } visu;
#define ANIM_NONE 0x00 #define ANIM_NONE 0x00
@@ -174,6 +219,7 @@ static void grfe_handler( u8_t *data, int len);
static void grfb_handler(u8_t *data, int len); static void grfb_handler(u8_t *data, int len);
static void grfs_handler(u8_t *data, int len); static void grfs_handler(u8_t *data, int len);
static void grfg_handler(u8_t *data, int len); static void grfg_handler(u8_t *data, int len);
static void grfa_handler(u8_t *data, int len);
static void visu_handler(u8_t *data, int len); static void visu_handler(u8_t *data, int len);
static void displayer_task(void* arg); static void displayer_task(void* arg);
@@ -205,6 +251,33 @@ static void displayer_task(void* arg);
ANIM_SCREEN_2 0x08 # For scrollonce only, screen 2 was scrolling ANIM_SCREEN_2 0x08 # For scrollonce only, screen 2 was scrolling
*/ */
/* classical visu not our specific version)
Parameters for the spectrum analyzer:
0 - Channels: stereo == 0, mono == 1
1 - Bandwidth: 0..22050Hz == 0, 0..11025Hz == 1
2 - Preemphasis in dB per KHz
Left channel parameters:
3 - Position in pixels
4 - Width in pixels
5 - orientation: left to right == 0, right to left == 1
6 - Bar width in pixels
7 - Bar spacing in pixels
8 - Clipping: show all subbands == 0, clip higher subbands == 1
9 - Bar intensity (greyscale): 1-3
10 - Bar cap intensity (greyscale): 1-3
Right channel parameters (not required for mono):
11-18 - same as left channel parameters
Parameters for the vumeter:
0 - Channels: stereo == 0, mono == 1
1 - Style: digital == 0, analog == 1
Left channel parameters:
2 - Position in pixels
3 - Width in pixels
Right channel parameters (not required for mono):
4-5 - same as left channel parameters
*/
/**************************************************************************************** /****************************************************************************************
* *
*/ */
@@ -226,6 +299,7 @@ bool sb_display_init(void) {
// create visu configuration // create visu configuration
visu.bar_gap = 1; visu.bar_gap = 1;
visu.speed = 100; visu.speed = 100;
visu.back.frame = calloc(1, (displayer.width * displayer.height) / 8);
dsps_fft2r_init_fc32(visu.fft, FFT_LEN); dsps_fft2r_init_fc32(visu.fft, FFT_LEN);
dsps_wind_hann_f32(visu.hanning, FFT_LEN); dsps_wind_hann_f32(visu.hanning, FFT_LEN);
@@ -300,7 +374,7 @@ static void send_server(void) {
pkt_header.length = htonl(sizeof(pkt_header) - 8); pkt_header.length = htonl(sizeof(pkt_header) - 8);
pkt_header.mode = ANIC_resp; pkt_header.mode = ANIC_resp;
send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
ANIC_resp = ANIM_NONE; ANIC_resp = ANIM_NONE;
} }
@@ -308,17 +382,21 @@ static void send_server(void) {
if (SETD_width) { if (SETD_width) {
struct SETD_header pkt_header; struct SETD_header pkt_header;
LOG_INFO("sending width %u", SETD_width);
memset(&pkt_header, 0, sizeof(pkt_header)); memset(&pkt_header, 0, sizeof(pkt_header));
memcpy(&pkt_header.opcode, "SETD", 4); memcpy(&pkt_header.opcode, "SETD", 4);
pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2 pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2
pkt_header.length = htonl(sizeof(pkt_header) + 2 - 8); pkt_header.length = htonl(sizeof(pkt_header) + 4 - 8);
u16_t height = GDS_GetHeight(display);
LOG_INFO("sending dimension %ux%u", SETD_width, height);
SETD_width = htons(SETD_width); SETD_width = htons(SETD_width);
send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); height = htons(height);
send_packet((uint8_t*) &SETD_width, 2);
send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
send_packet((uint8_t *) &SETD_width, 2);
send_packet((uint8_t *) &height, 2);
SETD_width = 0; SETD_width = 0;
} }
@@ -360,6 +438,8 @@ static bool handler(u8_t *data, int len){
grfs_handler(data, len); grfs_handler(data, len);
} else if (!strncmp((char*) data, "grfg", 4)) { } else if (!strncmp((char*) data, "grfg", 4)) {
grfg_handler(data, len); grfg_handler(data, len);
} else if (!strncmp((char*) data, "grfa", 4)) {
grfa_handler(data, len);
} else if (!strncmp((char*) data, "visu", 4)) { } else if (!strncmp((char*) data, "visu", 4)) {
visu_handler(data, len); visu_handler(data, len);
} else { } else {
@@ -495,7 +575,7 @@ 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 // we are not in control or we are displaying visu on a small screen, do not do screen update
if (visu.mode && !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;
} }
@@ -509,6 +589,16 @@ static void grfe_handler( u8_t *data, int len) {
// draw new frame, it might be less than full screen (small visu) // draw new frame, it might be less than full screen (small visu)
int width = ((len - sizeof(struct grfe_packet)) * 8) / displayer.height; int width = ((len - sizeof(struct grfe_packet)) * 8) / displayer.height;
// when doing screensaver, that frame becomes a visu background
if (!(visu.mode & VISU_ESP32)) {
visu.back.width = width;
memset(visu.back.frame, 0, (displayer.width * displayer.height) / 8);
memcpy(visu.back.frame, data + sizeof(struct grfe_packet), (width * displayer.height) / 8);
// this is a bit tricky but basically that checks if frame if full of 0
visu.back.active = *visu.back.frame || memcmp(visu.back.frame, visu.back.frame + 1, width - 1);
}
GDS_DrawBitmapCBR(display, data + sizeof(struct grfe_packet), width, displayer.height, GDS_COLOR_WHITE); GDS_DrawBitmapCBR(display, data + sizeof(struct grfe_packet), width, displayer.height, GDS_COLOR_WHITE);
GDS_Update(display); GDS_Update(display);
} }
@@ -548,7 +638,7 @@ static void grfs_handler(u8_t *data, int len) {
int size = len - sizeof(struct grfs_packet); int size = len - sizeof(struct grfs_packet);
int offset = htons(pkt->offset); int offset = htons(pkt->offset);
LOG_DEBUG("gfrs s:%u d:%u p:%u sp:%u by:%hu m:%hu w:%hu o:%hu", LOG_DEBUG("grfs s:%u d:%u p:%u sp:%u by:%hu m:%hu w:%hu o:%hu",
(int) pkt->screen, (int) pkt->screen,
(int) pkt->direction, // 1=left, 2=right (int) pkt->direction, // 1=left, 2=right
htonl(pkt->pause), // in ms htonl(pkt->pause), // in ms
@@ -573,9 +663,6 @@ static void grfs_handler(u8_t *data, int len) {
scroller.first = true; scroller.first = true;
scroller.overflow = false; 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);
// set scroller steps & beginning // set scroller steps & beginning
if (pkt->direction == 1) { if (pkt->direction == 1) {
scroller.scrolled = 0; scroller.scrolled = 0;
@@ -612,6 +699,7 @@ static void grfg_handler(u8_t *data, int len) {
// size of scrollable area (less than background) // size of scrollable area (less than background)
scroller.width = htons(pkt->width); scroller.width = htons(pkt->width);
scroller.back.width = ((len - sizeof(struct grfg_packet)) * 8) / displayer.height;
memcpy(scroller.back.frame, data + sizeof(struct grfg_packet), len - sizeof(struct grfg_packet)); memcpy(scroller.back.frame, data + sizeof(struct grfg_packet), len - sizeof(struct grfg_packet));
// update display asynchronously (frames are organized by columns) // update display asynchronously (frames are organized by columns)
@@ -636,6 +724,52 @@ static void grfg_handler(u8_t *data, int len) {
vTaskResume(displayer.task); vTaskResume(displayer.task);
} }
/****************************************************************************************
* Artwork
*/
static void grfa_handler(u8_t *data, int len) {
struct grfa_packet *pkt = (struct grfa_packet*) data;
int size = len - sizeof(struct grfa_packet);
int offset = htonl(pkt->offset);
int length = htonl(pkt->length);
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);
return;
}
// new grfa artwork, allocate memory
if (!offset) {
// same trick to clean current/previous window
if (artwork.size) {
GDS_ClearWindow(display, artwork.x, artwork.y, -1, -1, GDS_COLOR_BLACK);
artwork.size = 0;
}
// now use new parameters
artwork.x = htons(pkt->x);
artwork.y = htons(pkt->y);
if (artwork.data) free(artwork.data);
artwork.data = malloc(length);
}
// copy artwork data
memcpy(artwork.data + offset, data + sizeof(struct grfa_packet), size);
artwork.size += size;
if (artwork.size == length) {
GDS_ClearWindow(display, artwork.x, artwork.y, -1, -1, GDS_COLOR_BLACK);
GDS_DrawJPEG(display, artwork.data, artwork.x, artwork.y, artwork.y < SB_HEIGHT ? (GDS_IMAGE_RIGHT | GDS_IMAGE_TOP) : GDS_IMAGE_CENTER);
free(artwork.data);
artwork.data = NULL;
}
LOG_INFO("gfra l:%u x:%hu, y:%hu, o:%u s:%u", length, artwork.x, artwork.y, offset, size);
}
/**************************************************************************************** /****************************************************************************************
* Update visualization bars * Update visualization bars
*/ */
@@ -643,8 +777,10 @@ static void visu_update(void) {
// no need to protect against no woning the display as we are playing // no need to protect against no woning the display as we are playing
if (pthread_mutex_trylock(&visu_export.mutex)) return; if (pthread_mutex_trylock(&visu_export.mutex)) return;
int mode = visu.mode & ~VISU_ESP32;
// not enough samples // not enough samples
if (visu_export.level < (visu.mode == VISU_VUMETER ? RMS_LEN : FFT_LEN) * 2 && visu_export.running) { if (visu_export.level < (mode == VISU_VUMETER ? RMS_LEN : FFT_LEN) * 2 && visu_export.running) {
pthread_mutex_unlock(&visu_export.mutex); pthread_mutex_unlock(&visu_export.mutex);
return; return;
} }
@@ -654,7 +790,7 @@ static void visu_update(void) {
if (visu_export.running) { if (visu_export.running) {
if (visu.mode == VISU_VUMETER) { if (mode == VISU_VUMETER) {
s16_t *iptr = visu_export.buffer; s16_t *iptr = visu_export.buffer;
// calculate sum(L²+R²), try to not overflow at the expense of some precision // calculate sum(L²+R²), try to not overflow at the expense of some precision
@@ -667,7 +803,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 = 32 * (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 > 31) visu.bars[i].current = 31;
else if (visu.bars[i].current < 0) visu.bars[i].current = 0; else if (visu.bars[i].current < 0) visu.bars[i].current = 0;
} }
@@ -709,7 +845,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 = 32 * (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 > 31) visu.bars[i].current = 31;
else if (visu.bars[i].current < 0) visu.bars[i].current = 0; else if (visu.bars[i].current < 0) visu.bars[i].current = 0;
} }
@@ -725,6 +861,11 @@ static void visu_update(void) {
for (int i = visu.n; --i >= 0;) clear = max(clear, visu.bars[i].max); for (int i = visu.n; --i >= 0;) clear = max(clear, visu.bars[i].max);
if (clear) GDS_ClearExt(display, false, false, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1); if (clear) GDS_ClearExt(display, false, false, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
// draw background if we are in screensaver mode
if (!(visu.mode & VISU_ESP32) && visu.back.active) {
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 // 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);
@@ -778,11 +919,17 @@ static void visu_handler( u8_t *data, int len) {
visu.mode = pkt->which; visu.mode = pkt->which;
// little trick to clean the taller screens when switching visu // little trick to clean the taller screens when switching visu
if (visu.row >= SB_HEIGHT) GDS_ClearExt(display, false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1); if (visu.row >= SB_HEIGHT) GDS_ClearExt(display, false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
if (visu.mode) { if (visu.mode) {
// these will be overidden if necessary
visu.col = visu.border = 0;
visu.width = displayer.width;
// what type of visu
if (visu.mode & VISU_ESP32) {
if (pkt->count >= 4) { if (pkt->count >= 4) {
// small visu, then go were we are told to // more than 4 parameters, this is small visu, then go were we are told to
pkt->height = htonl(pkt->height); pkt->height = htonl(pkt->height);
pkt->row = htonl(pkt->row); pkt->row = htonl(pkt->row);
pkt->col = htonl(pkt->col); pkt->col = htonl(pkt->col);
@@ -794,21 +941,25 @@ static void visu_handler( u8_t *data, int len) {
visu.border = htonl(pkt->border); visu.border = htonl(pkt->border);
bars = htonl(pkt->bars); bars = htonl(pkt->bars);
visu.spectrum_scale = htonl(pkt->spectrum_scale) / 100.; visu.spectrum_scale = htonl(pkt->spectrum_scale) / 100.;
// might have a race condition with scroller message, so update width in case
if (scroller.active) scroller.back.width = displayer.width - visu.width;
} else { } else {
// full screen visu, try to use bottom screen if available // full screen visu, try to use bottom screen if available
visu.width = displayer.width;
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.col = visu.border = 0;
visu.row = GDS_GetHeight(display) - visu.height;
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 {
// 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));
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_SPECTRUM) { if ((visu.mode & ~VISU_ESP32) == VISU_SPECTRUM) {
visu.n = bars ? bars : MAX_BARS; visu.n = bars ? bars : MAX_BARS;
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);
@@ -836,7 +987,7 @@ static void visu_handler( u8_t *data, int len) {
// reset bars maximum // reset bars maximum
for (int i = visu.n; --i >= 0;) visu.bars[i].max = 0; for (int i = visu.n; --i >= 0;) visu.bars[i].max = 0;
GDS_ClearExt(display, false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row - visu.height - 1); GDS_ClearExt(display, false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
LOG_INFO("Visualizer with %u bars of width %d:%d:%d:%d (%w:%u,h:%u,c:%u,r:%u,s:%.02f)", visu.n, visu.bar_border, visu.bar_width, visu.bar_gap, visu.border, visu.width, visu.height, visu.col, visu.row, visu.spectrum_scale); LOG_INFO("Visualizer with %u bars of width %d:%d:%d:%d (%w:%u,h:%u,c:%u,r:%u,s:%.02f)", visu.n, visu.bar_border, visu.bar_width, visu.bar_gap, visu.border, visu.width, visu.height, visu.col, visu.row, visu.spectrum_scale);
} else { } else {

Binary file not shown.

View File

@@ -14,6 +14,8 @@ my $VISUALIZER_NONE = 0;
my $VISUALIZER_VUMETER = 1; my $VISUALIZER_VUMETER = 1;
my $VISUALIZER_SPECTRUM_ANALYZER = 2; my $VISUALIZER_SPECTRUM_ANALYZER = 2;
my $VISUALIZER_WAVEFORM = 3; my $VISUALIZER_WAVEFORM = 3;
my $VISUALIZER_VUMETER_ESP32 = 0x11;
my $VISUALIZER_SPECTRUM_ANALYZER_ESP32 = 0x12;
{ {
#__PACKAGE__->mk_accessor('array', 'modes'); #__PACKAGE__->mk_accessor('array', 'modes');
@@ -71,7 +73,12 @@ sub displayWidth {
} }
if ($display->widthOverride) { if ($display->widthOverride) {
my $artwork = $prefs->client($client)->get('artwork');
if ($artwork->{'enable'} && $artwork->{'y'} < 32 && $client->isPlaying) {
return $artwork->{x} + ($display->modes->[$mode || 0]{_width} || 0);
} else {
return $display->widthOverride + ($display->modes->[$mode || 0]{_width} || 0); return $display->widthOverride + ($display->modes->[$mode || 0]{_width} || 0);
}
} else { } else {
return $display->modes->[$mode || 0]{width}; return $display->modes->[$mode || 0]{width};
} }
@@ -100,10 +107,22 @@ sub build_modes {
my $client = shift->client; my $client = shift->client;
my $cprefs = $prefs->client($client); my $cprefs = $prefs->client($client);
my $width = shift || $cprefs->get('width') || 128; my $width = $cprefs->get('width') || 128;
my $artwork = $cprefs->get('artwork');
# if artwork is in main display, reduce width
$width = $artwork->{'x'} if $artwork->{'enable'} && $artwork->{y} < 32;
my $small_VU = $cprefs->get('small_VU'); my $small_VU = $cprefs->get('small_VU');
my $spectrum = $cprefs->get('spectrum'); my $spectrum = $cprefs->get('spectrum');
my $small_spectrum_pos = { x => $width - int ($spectrum->{small}->{size} * $width / 100),
width => int ($spectrum->{small}->{size} * $width / 100),
};
my $small_VU_pos = { x => $width - int ($small_VU * $width / 100),
width => int ($small_VU * $width / 100),
};
my @modes = ( my @modes = (
# mode 0 # mode 0
{ desc => ['BLANK'], { desc => ['BLANK'],
@@ -135,41 +154,41 @@ sub build_modes {
params => [$VISUALIZER_NONE] }, params => [$VISUALIZER_NONE] },
# mode 7 # mode 7
{ desc => ['VISUALIZER_VUMETER_SMALL'], { desc => ['VISUALIZER_VUMETER_SMALL'],
bar => 0, secs => 0, width => $width, _width => int -($small_VU*$width/100), bar => 0, secs => 0, width => $width, _width => -$small_VU_pos->{'width'},
# extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space) # extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space)
params => [$VISUALIZER_VUMETER, int ($small_VU*$width/100), 32, int -($small_VU*$width/100), 0, 2] }, params => [$VISUALIZER_VUMETER_ESP32, $small_VU_pos->{'width'}, 32, $small_VU_pos->{'x'}, 0, 2] },
# mode 8 # mode 8
{ desc => ['VISUALIZER_SPECTRUM_ANALYZER_SMALL'], { desc => ['VISUALIZER_SPECTRUM_ANALYZER_SMALL'],
bar => 0, secs => 0, width => $width, _width => int -($spectrum->{small}->{size}*$width/100), bar => 0, secs => 0, width => $width, _width => -$small_spectrum_pos->{'width'},
# extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space, bars) # extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space, #bars, 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}] }, params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, $small_spectrum_pos->{width}, 32, $small_spectrum_pos->{'x'}, 0, 2, $small_spectrum_pos->{'width'} / $spectrum->{small}->{band}, $spectrum->{scale}] },
# 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] }, params => [$VISUALIZER_VUMETER_ESP32] },
# mode 10 # mode 10
{ 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, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] }, params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
# mode 11 # mode 11
{ 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] }, params => [$VISUALIZER_VUMETER_ESP32] },
# mode 12 # mode 12
{ 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, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] }, params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
# mode 13 # mode 13
{ 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] }, params => [$VISUALIZER_VUMETER_ESP32] },
# mode 14 # mode 14
{ 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, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] }, params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
); );
return \@modes; return \@modes;

View File

@@ -1,20 +0,0 @@
[% PROCESS settings/header.html %]
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_BANNER" %]
<div>[% "PLUGIN_SQUEEZEESP32_BANNER_TEXT" | string %]</div>
[% END %]
<div class="prefDesc">
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_WIDTH" desc="PLUGIN_SQUEEZEESP32_WIDTH_DESC" %]
<input type="text" class="stdedit" name="pref_width" id="pref_width" value="[% prefs.pref_width %]" size="3">
[% END %]
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE" desc="PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE_DESC" %]
<input type="number" min="10" max= "50" step="5" class="stdedit" name="pref_spectrum_scale" id="pref_spectrum_scale" value="[% prefs.pref_spectrum_scale %]" size="3">
[% END %]
</div>
[% PROCESS settings/footer.html %]

View File

@@ -24,5 +24,14 @@
<input type="text" class="stdedit" name="pref_spectrum_full_band" id="spectrum_full_band" value="[% prefs.pref_spectrum.full.band %]" size="3"> <input type="text" class="stdedit" name="pref_spectrum_full_band" id="spectrum_full_band" value="[% prefs.pref_spectrum.full.band %]" size="3">
[% END %] [% END %]
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_ARTWORK" desc="PLUGIN_SQUEEZEESP32_ARTWORK_DESC" %]
[% "PLUGIN_SQUEEZEESP32_ARTWORK_ENABLE" | string %]&nbsp
<input type="checkbox" name="pref_artwork_enable" [% IF prefs.pref_artwork.enable %] checked [% END %]>
[% "PLUGIN_SQUEEZEESP32_ARTWORK_X" | string %]&nbsp
<input type="text" class="stdedit" name="pref_artwork_x" id="artwork_x" value="[% prefs.pref_artwork.x %]" size="2">
[% "PLUGIN_SQUEEZEESP32_ARTWORK_Y" | string %]&nbsp
<input type="text" class="stdedit" name="pref_artwork_y" id="artwork_y" value="[% prefs.pref_artwork.y %]" size="2">
[% END %]
[% PROCESS settings/footer.html %] [% PROCESS settings/footer.html %]

View File

@@ -21,16 +21,18 @@ sub playerSettingsFrame {
my $value; my $value;
my $id = unpack('C', $$data_ref); my $id = unpack('C', $$data_ref);
# New SETD command 0xfe for display width # New SETD command 0xfe for display width & height
if ($id == 0xfe) { if ($id == 0xfe) {
$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);
$client->display->modes($client->display->build_modes($value)); $client->display->modes($client->display->build_modes);
$client->display->widthOverride(1, $value); $client->display->widthOverride(1, $value);
$client->update; $client->update;
} }
$log->info("Setting player width $value 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); $client->SUPER::playerSettingsFrame($data_ref);
@@ -40,4 +42,10 @@ sub hasScrolling {
return 1; return 1;
} }
sub reconnect {
my $client = shift;
$client->pluginData('artwork_md5', '');
$client->SUPER::reconnect(@_);
}
1; 1;

View File

@@ -30,7 +30,7 @@ sub page {
sub prefs { sub prefs {
my ($class, $client) = @_; my ($class, $client) = @_;
my @prefs = qw(width small_VU spectrum); my @prefs = qw(width small_VU spectrum artwork);
return ($prefs->client($client), @prefs); return ($prefs->client($client), @prefs);
} }
@@ -47,8 +47,20 @@ sub handler {
full => { band => $paramRef->{'pref_spectrum_full_band'} }, full => { band => $paramRef->{'pref_spectrum_full_band'} },
}; };
$cprefs->set('spectrum', $spectrum); $cprefs->set('spectrum', $spectrum);
my $artwork = { enable => $paramRef->{'pref_artwork_enable'},
x => $paramRef->{'pref_artwork_x'},
y => $paramRef->{'pref_artwork_y'},
};
$cprefs->set('artwork', $artwork);
$client->display->modes($client->display->build_modes); $client->display->modes($client->display->build_modes);
$client->display->update; $client->display->update;
# force update or disable artwork
if ($artwork->{'enable'}) {
Plugins::SqueezeESP32::Plugin::update_artwork($client, 1);
} else {
Plugins::SqueezeESP32::Plugin::disable_artwork($client);
}
} }
# as there is nothing captured, we need to re-set these variables # as there is nothing captured, we need to re-set these variables
@@ -56,9 +68,10 @@ sub handler {
# here I don't know why you need to set again spectrum which is a reference # 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 # 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 # some are copies of value, some are references, can't figure out. This whole
# logic of "Settings" is beyond me and I really hate it # logic of "Settings" is beyond me and I really hate it
$paramRef->{'pref_spectrum'} = $cprefs->get('spectrum'); $paramRef->{'pref_spectrum'} = $cprefs->get('spectrum');
$paramRef->{'pref_artwork'} = $cprefs->get('artwork');
return $class->SUPER::handler($client, $paramRef); return $class->SUPER::handler($client, $paramRef);
} }

View File

@@ -3,8 +3,12 @@ package Plugins::SqueezeESP32::Plugin;
use strict; use strict;
use base qw(Slim::Plugin::Base); use base qw(Slim::Plugin::Base);
use Digest::MD5 qw(md5);
use List::Util qw(min);
use Slim::Utils::Prefs; use Slim::Utils::Prefs;
use Slim::Utils::Log; use Slim::Utils::Log;
use Slim::Web::ImageProxy;
my $prefs = preferences('plugin.squeezeesp32'); my $prefs = preferences('plugin.squeezeesp32');
@@ -28,6 +32,73 @@ sub initPlugin {
$class->SUPER::initPlugin(@_); $class->SUPER::initPlugin(@_);
Slim::Networking::Slimproto::addPlayerClass($class, 100, 'squeezeesp32', { client => 'Plugins::SqueezeESP32::Player', display => 'Plugins::SqueezeESP32::Graphics' }); Slim::Networking::Slimproto::addPlayerClass($class, 100, 'squeezeesp32', { client => 'Plugins::SqueezeESP32::Player', display => 'Plugins::SqueezeESP32::Graphics' });
$log->info("Added class 100 for SqueezeESP32"); $log->info("Added class 100 for SqueezeESP32");
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['newmetadata'] ] );
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'newsong'] ]);
}
sub onNotification {
my $request = shift;
my $client = $request->client;
my $reqstr = $request->getRequestString();
$log->info("artwork update notification $reqstr");
#my $path = $request->getParam('_path');
update_artwork($client);
}
sub update_artwork {
my $client = shift;
my $force = shift || 0;
my $cprefs = $prefs->client($client);
my $artwork = $cprefs->get('artwork');
return unless $client->model eq 'squeezeesp32' && $artwork->{'enable'};
my $s = $artwork->{'y'} >= 32 ? $cprefs->get('height') - $artwork->{'y'} : 32;
$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);
send_artwork($client, undef, \$body) if $body;
}
sub send_artwork {
my ($client, $force, $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;
$client->pluginData('artwork', $dataref);
$client->pluginData('artwork_md5', $md5);
my $artwork = $prefs->client($client)->get('artwork');
my $length = length $$dataref;
my $offset = 0;
$log->info("got resized artwork (length: ", length $$dataref, ")");
my $header = pack('Nnn', $length, $artwork->{'x'}, $artwork->{'y'});
while ($length > 0) {
$length = 1280 if $length > 1280;
$log->info("sending grfa $length");
my $data = $header . pack('N', $offset) . substr( $$dataref, 0, $length, '' );
$client->sendFrame( grfa => \$data );
$offset += $length;
$length = length $$dataref;
}
}
sub disable_artwork {
my ($client) = @_;
my $header = pack('N', 0);
$client->sendFrame( grfa => \$header );
} }
1; 1;

View File

@@ -1,30 +0,0 @@
package Plugins::SqueezeESP32::Settings;
use base qw(Slim::Web::Settings);
use strict;
use Slim::Utils::Prefs;
use Slim::Utils::Log;
my $log = logger('plugin.squeezeesp32');
sub name {
return 'PLUGIN_SQUEEZEESP32';
}
sub page {
return 'plugins/SqueezeESP32/settings/basic.html';
}
sub prefs {
return (preferences('plugin.squeezeesp32'), qw(width spectrum_scale));
}
sub handler {
my ($class, $client, $params, $callback, @args) = @_;
$callback->($client, $params, $class->SUPER::handler($client, $params), @args);
}
1;

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.31</version> <version>0.50</version>
<creator>Philippe</creator> <creator>Philippe</creator>
</extensions> </extensions>

View File

@@ -53,3 +53,19 @@ PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND
PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND_DESC PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND_DESC
EN The number of bands is the width of the screen divided by this factor EN The number of bands is the width of the screen divided by this factor
PLUGIN_SQUEEZEESP32_ARTWORK
EN Artwork
PLUGIN_SQUEEZEESP32_ARTWORK_DESC
EN When Y position is less than 32, then artwork is display at the right of the main screen and x defines the starting position
EN Using artwork on less than 16-levels grayscale display if really poor quality
PLUGIN_SQUEEZEESP32_ARTWORK_ENABLE
EN Enable
PLUGIN_SQUEEZEESP32_ARTWORK_X
EN X
PLUGIN_SQUEEZEESP32_ARTWORK_Y
EN Y

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.31" name="SqueezeESP32" minTarget="7.5" maxTarget="*"> <plugin version="0.50" 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>6dc35a0f9f9b287d205f7532cbb642b08407a284</sha> <sha>47feaf69a40ad4f87c58b34212d71e60dca99d3e</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>