diff --git a/components/display/core/gds.c b/components/display/core/gds.c index d315b027..26242a60 100644 --- a/components/display/core/gds.c +++ b/components/display/core/gds.c @@ -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 ) { + // -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 if (Device->ClearWindow) { Device->ClearWindow( Device, x1, y1, x2, y2, Color ); diff --git a/components/display/core/gds_image.c b/components/display/core/gds_image.c index a5919f74..ebd274e1 100644 --- a/components/display/core/gds_image.c +++ b/components/display/core/gds_image.c @@ -218,8 +218,10 @@ bool GDS_DrawJPEG( struct GDS_Device* Device, uint8_t *Source, int x, int y, int // then place it 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; - + else if (Fit & GDS_IMAGE_BOTTOM) Context.YOfs = Device->Height - Context.Height; + Context.XMin = x - Context.XOfs; Context.YMin = y - Context.YOfs; diff --git a/components/display/core/gds_image.h b/components/display/core/gds_image.h index fa87001a..0becd94a 100644 --- a/components/display/core/gds_image.h +++ b/components/display/core/gds_image.h @@ -9,8 +9,11 @@ struct GDS_Device; 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_RIGHT 0x04 +#define GDS_IMAGE_TOP 0x00 +#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 diff --git a/components/squeezelite/display.c b/components/squeezelite/display.c index 555ee983..c85cf4a2 100644 --- a/components/squeezelite/display.c +++ b/components/squeezelite/display.c @@ -62,7 +62,9 @@ struct grfg_packet { struct grfa_packet { char opcode[4]; - u32_t length; + u32_t length; + u16_t x; + u16_t y; u32_t offset; }; @@ -140,6 +142,7 @@ static struct scroller_s { static struct { u8_t *data; u32_t size; + u16_t x, y; } artwork; #define MAX_BARS 32 @@ -313,7 +316,7 @@ static void send_server(void) { pkt_header.length = htonl(sizeof(pkt_header) - 8); 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; } @@ -321,18 +324,22 @@ static void send_server(void) { if (SETD_width) { struct SETD_header pkt_header; - LOG_INFO("sending width %u", SETD_width); - memset(&pkt_header, 0, sizeof(pkt_header)); memcpy(&pkt_header.opcode, "SETD", 4); 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); - send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); - send_packet((uint8_t*) &SETD_width, 2); - + height = htons(height); + + 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; } @@ -661,10 +668,10 @@ static void grfa_handler(u8_t *data, int len) { int offset = htonl(pkt->offset); int length = htonl(pkt->length); - LOG_INFO("gfra l:%u o:%u s:%u", length, offset, size); - // new grfa artwork, allocate memory if (!offset) { + artwork.x = htons(pkt->x); + artwork.y = htons(pkt->y); if (artwork.data) free(artwork.data); artwork.data = malloc(length); artwork.size = 0; @@ -674,8 +681,14 @@ static void grfa_handler(u8_t *data, int len) { memcpy(artwork.data + offset, data + sizeof(struct grfa_packet), size); artwork.size += size; if (artwork.size == length) { - GDS_DrawJPEG(display, artwork.data, 0, 32, GDS_IMAGE_CENTER); - } + GDS_ClearWindow(display, artwork.x, artwork.y, -1, -1, GDS_COLOR_BLACK); + GDS_DrawJPEG(display, artwork.data, artwork.x, artwork.y, artwork.y < 32 ? (GDS_IMAGE_RIGHT | GDS_IMAGE_TOP) : GDS_IMAGE_CENTER); + free(artwork.data); + artwork.data = NULL; + artwork.size = 0; + } + + LOG_INFO("gfra l:%u x:%hu, y:%hu, o:%u s:%u", length, artwork.x, artwork.y, offset, size); } /**************************************************************************************** @@ -820,7 +833,7 @@ static void visu_handler( u8_t *data, int len) { visu.mode = pkt->which; // 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 (pkt->count >= 4) { @@ -878,7 +891,7 @@ static void visu_handler( u8_t *data, int len) { // reset bars maximum 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); } else { diff --git a/plugin/SqueezeESP32.zip b/plugin/SqueezeESP32.zip index 871345cc..2a3b0eeb 100644 Binary files a/plugin/SqueezeESP32.zip and b/plugin/SqueezeESP32.zip differ diff --git a/plugin/SqueezeESP32/Graphics.pm b/plugin/SqueezeESP32/Graphics.pm index 225c907b..a8dead59 100644 --- a/plugin/SqueezeESP32/Graphics.pm +++ b/plugin/SqueezeESP32/Graphics.pm @@ -71,7 +71,12 @@ sub displayWidth { } if ($display->widthOverride) { - return $display->widthOverride + ($display->modes->[$mode || 0]{_width} || 0); + my $artwork = $prefs->client($client)->get('artwork'); + if ($artwork->{'enable'} && $artwork->{'y'} < 32) { + return $artwork->{x} + ($display->modes->[$mode || 0]{_width} || 0); + } else { + return $display->widthOverride + ($display->modes->[$mode || 0]{_width} || 0); + } } else { return $display->modes->[$mode || 0]{width}; } @@ -101,9 +106,21 @@ sub build_modes { my $cprefs = $prefs->client($client); my $width = shift || $cprefs->get('width') || 128; + my $artwork = $cprefs->get('artwork'); + + # if artwork is in main display, reduce width + $width = $artwork->{'x'} - 1 if $artwork->{'enable'} && $artwork->{y} < 32; + my $small_VU = $cprefs->get('small_VU'); 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 = ( # mode 0 { desc => ['BLANK'], @@ -135,14 +152,14 @@ sub build_modes { params => [$VISUALIZER_NONE] }, # mode 7 { 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) - params => [$VISUALIZER_VUMETER, int ($small_VU*$width/100), 32, int -($small_VU*$width/100), 0, 2] }, + params => [$VISUALIZER_VUMETER, $small_VU_pos->{'width'}, 32, $small_VU_pos->{'x'}, 0, 2] }, # mode 8 { 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) - 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, $small_spectrum_pos->{width}, 32, $small_spectrum_pos->{'x'}, 0, 2, $small_spectrum_pos->{'width'} / $spectrum->{small}->{band}, $spectrum->{scale}] }, # mode 9 { desc => ['VISUALIZER_VUMETER'], bar => 0, secs => 0, width => $width, diff --git a/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/basic.html b/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/basic.html deleted file mode 100644 index fdf77916..00000000 --- a/plugin/SqueezeESP32/HTML/EN/plugins/SqueezeESP32/settings/basic.html +++ /dev/null @@ -1,20 +0,0 @@ -[% PROCESS settings/header.html %] - - [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_BANNER" %] -
[% "PLUGIN_SQUEEZEESP32_BANNER_TEXT" | string %]
- [% END %] - -
- - [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_WIDTH" desc="PLUGIN_SQUEEZEESP32_WIDTH_DESC" %] - - [% END %] - - [% WRAPPER setting title="PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE" desc="PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE_DESC" %] - - [% END %] - - -
- -[% PROCESS settings/footer.html %] diff --git a/plugin/SqueezeESP32/Player.pm b/plugin/SqueezeESP32/Player.pm index 946eb303..d3692e2e 100644 --- a/plugin/SqueezeESP32/Player.pm +++ b/plugin/SqueezeESP32/Player.pm @@ -20,8 +20,8 @@ sub playerSettingsFrame { my $value; my $id = unpack('C', $$data_ref); - - # New SETD command 0xfe for display width + + # New SETD command 0xfe for display width & height if ($id == 0xfe) { $value = (unpack('Cn', $$data_ref))[1]; if ($value > 100 && $value < 400) { @@ -30,7 +30,9 @@ sub playerSettingsFrame { $client->display->widthOverride(1, $value); $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); @@ -40,4 +42,11 @@ sub hasScrolling { return 1; } + +sub directMetadata { + my $client = shift; + $client->SUPER::directMetadata(@_); + Slim::Control::Request::notifyFromArray( $client, [ 'newmetadata' ] ); +} + 1; diff --git a/plugin/SqueezeESP32/PlayerSettings.pm b/plugin/SqueezeESP32/PlayerSettings.pm index 4c88dcfd..1ff4f7b9 100644 --- a/plugin/SqueezeESP32/PlayerSettings.pm +++ b/plugin/SqueezeESP32/PlayerSettings.pm @@ -30,7 +30,7 @@ sub page { sub prefs { my ($class, $client) = @_; - my @prefs = qw(width small_VU spectrum); + my @prefs = qw(width small_VU spectrum artwork); return ($prefs->client($client), @prefs); } @@ -47,6 +47,11 @@ sub handler { full => { band => $paramRef->{'pref_spectrum_full_band'} }, }; $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->update; } @@ -56,9 +61,10 @@ sub handler { # here I don't know why you need to set again spectrum which is a reference # to a hash. Using $paramRef->{prefs} does not work either. It seems that - # soem are copies of value, some are references, can't figure out.This whole + # 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 $paramRef->{'pref_spectrum'} = $cprefs->get('spectrum'); + $paramRef->{'pref_artwork'} = $cprefs->get('artwork'); return $class->SUPER::handler($client, $paramRef); } diff --git a/plugin/SqueezeESP32/Plugin.pm b/plugin/SqueezeESP32/Plugin.pm index 5078f66c..22a462d6 100644 --- a/plugin/SqueezeESP32/Plugin.pm +++ b/plugin/SqueezeESP32/Plugin.pm @@ -3,8 +3,12 @@ package Plugins::SqueezeESP32::Plugin; use strict; use base qw(Slim::Plugin::Base); + +use Digest::MD5 qw(md5); +use List::Util qw(min); use Slim::Utils::Prefs; use Slim::Utils::Log; +use Slim::Web::ImageProxy; my $prefs = preferences('plugin.squeezeesp32'); @@ -28,6 +32,61 @@ sub initPlugin { $class->SUPER::initPlugin(@_); Slim::Networking::Slimproto::addPlayerClass($class, 100, 'squeezeesp32', { client => 'Plugins::SqueezeESP32::Player', display => 'Plugins::SqueezeESP32::Graphics' }); $log->info("Added class 100 for SqueezeESP32"); + + Slim::Control::Request::subscribe(\&update_artwork, [ ['newmetadata'] ] ); + Slim::Control::Request::subscribe(\&update_artwork, [ ['playlist'], ['open', 'newsong'] ]); } +sub update_artwork { + my $request = shift; + my $client = $request->client; + my $cprefs = $prefs->client($client); + my $artwork = $cprefs->get('artwork'); + + return unless $client->model eq 'squeezeesp32' && $artwork->{'enable'}; + + my $reqstr = $request->getRequestString(); + #my $path = $request->getParam('_path'); + + 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, undef, \&send_artwork, undef, HTTP::Response->new); + + send_artwork($client, undef, \$body) if $body; + + $log->info("artwork update notification $reqstr with $path"); +} + +sub send_artwork { + 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; + + $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; + } +} + 1; diff --git a/plugin/SqueezeESP32/Settings.pm b/plugin/SqueezeESP32/Settings.pm deleted file mode 100644 index 1ef18504..00000000 --- a/plugin/SqueezeESP32/Settings.pm +++ /dev/null @@ -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; diff --git a/plugin/SqueezeESP32/SqueezeESP32.zip b/plugin/SqueezeESP32/SqueezeESP32.zip new file mode 100644 index 00000000..2a3b0eeb Binary files /dev/null and b/plugin/SqueezeESP32/SqueezeESP32.zip differ diff --git a/plugin/SqueezeESP32/install.xml b/plugin/SqueezeESP32/install.xml index 338f4283..14f6f93a 100644 --- a/plugin/SqueezeESP32/install.xml +++ b/plugin/SqueezeESP32/install.xml @@ -10,6 +10,6 @@ PLUGIN_SQUEEZEESP32 PLUGIN_SQUEEZEESP32_DESC Plugins::SqueezeESP32::Plugin - 0.31 + 0.40 Philippe diff --git a/plugin/SqueezeESP32/strings.txt b/plugin/SqueezeESP32/strings.txt index 1dd9e492..d5ed82c2 100644 --- a/plugin/SqueezeESP32/strings.txt +++ b/plugin/SqueezeESP32/strings.txt @@ -53,3 +53,19 @@ PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND_DESC 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 diff --git a/plugin/repo.xml b/plugin/repo.xml index 45a2e536..e0c81729 100644 --- a/plugin/repo.xml +++ b/plugin/repo.xml @@ -1,10 +1,10 @@ - + https://github.com/sle118/squeezelite-esp32 Philippe - 6dc35a0f9f9b287d205f7532cbb642b08407a284 + aa122a85db949c903ffa978d3e3b4ee3205e4ec2 philippe_44@outlook.com SqueezeESP32 additional player id (100) http://github.com/sle118/squeezelite-esp32/raw/master/plugin/SqueezeESP32.zip