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

Conflicts:
	components/raop/rtp.c
	components/squeezelite/display.c
This commit is contained in:
Sebastien
2020-04-06 10:39:55 -04:00
14 changed files with 364 additions and 88 deletions

View File

@@ -0,0 +1,203 @@
/**
* Copyright (c) 2017-2018 Tara Keeling
* 2020 Philippe G.
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <esp_heap_caps.h>
#include <esp_log.h>
#include "gds.h"
#include "gds_private.h"
#define SHADOW_BUFFER
#define PAGE_BLOCK 1024
#define min(a,b) (((a) < (b)) ? (a) : (b))
static char TAG[] = "SSD1322";
struct PrivateSpace {
uint8_t *iRAM, *Shadowbuffer;
uint8_t ReMap, PageSize;
uint8_t Offset;
};
// Functions are not declared to minimize # of lines
static void WriteDataByte( struct GDS_Device* Device, uint8_t Data ) {
Device->WriteData( Device, &Data, 1);
}
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, 0x15 );
Device->WriteData( Device, &Start, 1 );
Device->WriteData( Device, &End, 1 );
}
static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
Device->WriteCommand( Device, 0x75 );
Device->WriteData( Device, &Start, 1 );
Device->WriteData( Device, &End, 1 );
}
static void Update( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
// RAM is by columns of 4 pixels ...
SetColumnAddress( Device, Private->Offset, Private->Offset + Device->Width / 4 - 1);
#ifdef SHADOW_BUFFER
uint16_t *optr = (uint16_t*) Private->Shadowbuffer, *iptr = (uint16_t*) Device->Framebuffer;
bool dirty = false;
for (int r = 0, page = 0; r < Device->Height; r++) {
// look for change and update shadow (cheap optimization = width always / by 2)
for (int c = Device->Width / 2 / 2; --c >= 0;) {
if (*optr != *iptr) {
dirty = true;
*optr = *iptr;
}
iptr++; optr++;
}
// one line done, check for page boundary
if (++page == Private->PageSize) {
if (dirty) {
uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Private->Shadowbuffer + (r - page + 1) * Device->Width / 2);
SetRowAddress( Device, r - page + 1, r );
for (int i = page * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
//memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
Device->WriteCommand( Device, 0x5c );
Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 );
dirty = false;
}
page = 0;
}
}
#else
for (int r = 0; r < Device->Height; r += Private->PageSize) {
SetRowAddress( Device, r, r + Private->PageSize - 1 );
Device->WriteCommand( Device, 0x5c );
if (Private->iRAM) {
uint16_t *optr = (uint16_t*) Private->iRAM, *iptr = (uint16_t*) (Device->Framebuffer + r * Device->Width / 2);
for (int i = Private->PageSize * Device->Width / 2 / 2; --i >= 0; iptr++) *optr++ = (*iptr >> 8) | (*iptr << 8);
//memcpy(Private->iRAM, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 );
} else {
Device->WriteData( Device, Device->Framebuffer + r * Device->Width / 2, Private->PageSize * Device->Width / 2 );
}
}
#endif
}
static void SetHFlip( struct GDS_Device* Device, bool On ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
Private->ReMap = On ? (Private->ReMap & ~(1 << 1)) : (Private->ReMap | (1 << 1));
Device->WriteCommand( Device, 0xA0 );
Device->WriteData( Device, &Private->ReMap, 1 );
WriteDataByte( Device, 0x11 );
}
static void SetVFlip( struct GDS_Device *Device, bool On ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
Private->ReMap = On ? (Private->ReMap | (1 << 4)) : (Private->ReMap & ~(1 << 4));
Device->WriteCommand( Device, 0xA0 );
Device->WriteData( Device, &Private->ReMap, 1 );
WriteDataByte( Device, 0x11 );
}
static void DisplayOn( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAF ); }
static void DisplayOff( struct GDS_Device* Device ) { Device->WriteCommand( Device, 0xAE ); }
static void SetContrast( struct GDS_Device* Device, uint8_t Contrast ) {
Device->WriteCommand( Device, 0xC1 );
Device->WriteData( Device, &Contrast, 1 );
}
static bool Init( struct GDS_Device* Device ) {
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
// these displays seems to be layout centered (1 column = 4 pixels of 4 bits each, little endian)
Private->Offset = (480 - Device->Width) / 4 / 2;
// find a page size that is not too small is an integer of height
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2));
Private->PageSize = Device->Height / (Device->Height / Private->PageSize) ;
#ifdef SHADOW_BUFFER
Private->Shadowbuffer = malloc( Device->FramebufferSize );
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
#endif
Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
ESP_LOGI(TAG, "SSD1322 with offset %u, page %u, iRAM %p", Private->Offset, Private->PageSize, Private->iRAM);
// need to be off and disable display RAM
Device->DisplayOff( Device );
Device->WriteCommand( Device, 0xA5 );
// Display Offset
Device->WriteCommand( Device, 0xA2 );
WriteDataByte( Device, 0 );
// Display Start Line
Device->WriteCommand( Device, 0xA1 );
WriteDataByte( Device, 0x00 );
// set flip modes
Private->ReMap = 0;
Device->SetVFlip( Device, false );
Device->SetHFlip( Device, false );
// set Clocks
Device->WriteCommand( Device, 0xB3 );
WriteDataByte( Device, 0x91 );
// set MUX
Device->WriteCommand( Device, 0xCA );
WriteDataByte( Device, Device->Height - 1 );
// phase 1 & 2 period (needed?)
Device->WriteCommand( Device, 0xB1 );
WriteDataByte( Device, 0xE2 );
// set pre-charge V (needed?°)
Device->WriteCommand( Device, 0xBB );
WriteDataByte( Device, 0x1F );
// set COM deselect voltage (needed?)
Device->WriteCommand( Device, 0xBE );
WriteDataByte( Device, 0x07 );
// no Display Inversion
Device->WriteCommand( Device, 0xA6 );
// gone with the wind
Device->DisplayOn( Device );
Device->Update( Device );
return true;
}
static const struct GDS_Device SSD1322 = {
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
.SetVFlip = SetVFlip, .SetHFlip = SetHFlip,
.Update = Update, .Init = Init,
};
struct GDS_Device* SSD1322_Detect(char *Driver, struct GDS_Device* Device) {
if (!strcasestr(Driver, "SSD1322")) return NULL;
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
*Device = SSD1322;
Device->Depth = 4;
return Device;
}

View File

@@ -59,8 +59,8 @@ static EXT_RAM_ATTR struct {
static void displayer_task(void *args);
struct GDS_Device *display;
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect;
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, NULL };
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect;
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, NULL };
/****************************************************************************************
*

View File

@@ -435,7 +435,7 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi
ctx->ab_write = seqno-1;
ctx->ab_read = seqno;
ctx->flush_seqno = -1;
ctx->playing = true;
ctx->playing = true;
ctx->resent_req = ctx->resent_rec = ctx->silent_frames = ctx->discarded = 0;
playtime = ctx->synchro.time + ((rtptime - ctx->synchro.rtp) * 10) / (RAOP_SAMPLE_RATE / 100);
ctx->cmd_cb(RAOP_PLAY, playtime);
@@ -518,7 +518,7 @@ static void buffer_push_packet(rtp_t *ctx) {
// re-evaluate time in loop in case data callback blocks ...
now = gettime_ms();
// try to manage playtime so that we overflow as late as possible if we miss NTP (2^31 / 10 / 44100)
// try to manage playtime so that we overflow as late as possible if we miss NTP (2^31 / 10 / 44100)
curframe = ctx->audio_buffer + BUFIDX(ctx->ab_read);
playtime = ctx->synchro.time + ((curframe->rtptime - ctx->synchro.rtp) * 10) / (RAOP_SAMPLE_RATE / 100);

View File

@@ -208,10 +208,10 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
// first must make sure we started on time
if (raop_sync.win == SYNC_WIN_START) {
// 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 + ((output.frames_played_dmp - output.device_frames) * 10) / (RAOP_SAMPLE_RATE / 100);
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, error);
} else {
u32_t level = _buf_used(outputbuf);

View File

@@ -73,12 +73,15 @@ struct visu_packet {
u8_t which;
u8_t count;
union {
union {
struct {
struct {
u32_t width;
union {
struct {
u32_t bars;
u32_t spectrum_scale;
};
u32_t style;
};
u32_t style;
};
} full;
struct {
u32_t width;
@@ -180,6 +183,7 @@ static struct {
#define VISU_ESP32 0x10
static EXT_RAM_ATTR struct {
int bar_gap, bar_width, bar_border;
bool rotate;
struct {
int current, max;
int limit;
@@ -577,7 +581,7 @@ static void vfdc_handler( u8_t *_data, int bytes_read) {
/****************************************************************************************
* 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) {
void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x, int y, int width, bool rotate) {
// VU data is by columns and vertical flip to allow block offset
data += level * VU_WIDTH * VU_HEIGHT;
@@ -593,10 +597,18 @@ void draw_VU(struct GDS_Device * display, const uint8_t *data, int level, int x,
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_DrawPixelFastExt(display, r + x, c + y, *data++ >> scale);
if (visu.rotate) {
for (int r = 0; r < width; r++) {
for (int c = VU_HEIGHT; --c >= 0;) {
GDS_DrawPixelFastExt(display, c + x, r + y, *data++ >> scale);
}
}
} else {
for (int r = 0; r < width; r++) {
for (int c = 0; c < VU_HEIGHT; c++) {
GDS_DrawPixelFastExt(display, r + x, c + y, *data++ >> scale);
}
}
}
// need to manually set dirty flag as DrawPixel does not do it
@@ -613,22 +625,22 @@ static void grfe_handler( u8_t *data, int len) {
scroller.active = false;
// 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 < displayer.height) {
xSemaphoreGive(displayer.mutex);
return;
}
// are we in control
if (displayer.owned) {
// did we have something that might have write on the bottom of a SB_HEIGHT+ display
if (displayer.dirty) {
GDS_ClearExt(display, true);
// draw new frame, it might be less than full screen (small visu)
int width = ((len - sizeof(struct grfe_packet)) * 8) / displayer.height;
// did we have something that might have written on the bottom of a displayer's height + display
if (displayer.dirty || (artwork.enable && width == displayer.width && artwork.y < displayer.height)) {
GDS_Clear(display, GDS_COLOR_BLACK);
displayer.dirty = false;
}
// draw new frame, it might be less than full screen (small visu)
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;
@@ -735,7 +747,7 @@ 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);
// on small screen, visu has priority when full screen
if ((visu.mode & VISU_ESP32) && !visu.col && visu.row < SB_HEIGHT) return;
if ((visu.mode & VISU_ESP32) && !visu.col && visu.row < displayer.height) return;
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
@@ -755,7 +767,7 @@ static void grfg_handler(u8_t *data, int len) {
}
// now we can active scrolling, but only if we are not on a small screen
if (!visu.mode || visu.col || visu.row >= SB_HEIGHT) scroller.active = true;
if (!visu.mode || visu.col || visu.row >= displayer.height) scroller.active = true;
// if we just got a content update, let the scroller manage the screen
LOG_DEBUG("resuming scrolling task");
@@ -811,7 +823,7 @@ static void grfa_handler(u8_t *data, int len) {
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);
GDS_DrawJPEG(display, artwork.data, artwork.x, artwork.y, artwork.y < displayer.height ? (GDS_IMAGE_RIGHT | GDS_IMAGE_TOP) : GDS_IMAGE_CENTER);
free(artwork.data);
artwork.data = NULL;
}
@@ -852,7 +864,7 @@ static void visu_update(void) {
// convert to dB (1 bit remaining for getting X²/N, 60dB dynamic starting from 0dBFS = 3 bits back-off)
for (int i = visu.n; --i >= 0;) {
visu.bars[i].current = SB_HEIGHT * (0.01667f*10*log10f(0.0000001f + (visu.bars[i].current >> 1)) - 0.2543f);
visu.bars[i].current = visu.max * (0.01667f*10*log10f(0.0000001f + (visu.bars[i].current >> (visu_export.gain == FIXED_ONE ? 7 : 1))) - 0.2543f);
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;
}
@@ -894,7 +906,7 @@ static void visu_update(void) {
}
// convert to dB and bars, same back-off
if (power) visu.bars[i].current = SB_HEIGHT * (0.01667f*10*(log10f(power) - log10f(FFT_LEN/2*2)) - 0.2543f);
if (power) visu.bars[i].current = visu.max * (0.01667f*10*(log10f(power) - log10f(FFT_LEN/(visu_export.gain == FIXED_ONE ? 128 : 2)*2)) - 0.2543f);
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;
}
@@ -918,35 +930,49 @@ static void visu_update(void) {
if (mode != VISU_VUMETER || !visu.style) {
// there is much more optimization to be done here, like not redrawing bars unless needed
for (int i = visu.n; --i >= 0;) {
int x1 = visu.col + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap);
int y1 = visu.row + visu.height - 1;
// update maximum
if (visu.bars[i].current > visu.bars[i].max) visu.bars[i].max = visu.bars[i].current;
else if (visu.bars[i].max) visu.bars[i].max--;
else if (!clear) continue;
for (int j = 0; j <= visu.bars[i].current; j += 2)
if (visu.rotate) {
int x1 = visu.col;
int y1 = visu.row + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap);
for (int j = 0; j <= visu.bars[i].current; j += 2)
GDS_DrawLine(display, x1 + j, y1, x1 + j, y1 + visu.bar_width - 1, GDS_COLOR_WHITE);
if (visu.bars[i].max > 2) {
GDS_DrawLine(display, x1 + visu.bars[i].max, y1, x1 + visu.bars[i].max, y1 + visu.bar_width - 1, GDS_COLOR_WHITE);
if (visu.bars[i].max < visu.max - 1) GDS_DrawLine(display, x1 + visu.bars[i].max + 1, y1, x1 + visu.bars[i].max + 1, y1 + visu.bar_width - 1, GDS_COLOR_WHITE);
}
} else {
int x1 = visu.col + visu.border + visu.bar_border + i*(visu.bar_width + visu.bar_gap);
int y1 = visu.row + visu.height - 1;
for (int j = 0; j <= visu.bars[i].current; j += 2)
GDS_DrawLine(display, x1, y1 - j, x1 + visu.bar_width - 1, y1 - j, GDS_COLOR_WHITE);
if (visu.bars[i].max > 2) {
GDS_DrawLine(display, x1, y1 - visu.bars[i].max, x1 + visu.bar_width - 1, y1 - visu.bars[i].max, GDS_COLOR_WHITE);
GDS_DrawLine(display, x1, y1 - visu.bars[i].max + 1, x1 + visu.bar_width - 1, y1 - visu.bars[i].max + 1, GDS_COLOR_WHITE);
}
if (visu.bars[i].max > 2) {
GDS_DrawLine(display, x1, y1 - visu.bars[i].max, x1 + visu.bar_width - 1, y1 - visu.bars[i].max, GDS_COLOR_WHITE);
if (visu.bars[i].max < visu.max - 1) 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);
int width = visu.rotate ? visu.height : visu.width;
draw_VU(display, vu_bitmap, visu.bars[0].current, 0, visu.row, width / 2, visu.rotate);
draw_VU(display, vu_bitmap, visu.bars[1].current, width / 2, visu.row, width / 2, visu.rotate);
} else {
int level = (visu.bars[0].current + visu.bars[1].current) / 2;
draw_VU(display, vu_bitmap, level, 0, visu.row, displayer.width);
draw_VU(display, vu_bitmap, level, 0, visu.row, visu.rotate ? visu.height : visu.width, visu.rotate);
}
}
/****************************************************************************************
* Visu packet handler
* Calculate spectrum spread
*/
void spectrum_limits(int min, int n, int pos) {
static void spectrum_limits(int min, int n, int pos) {
if (n / 2) {
int step = ((DISPLAY_BW - min) * visu.spectrum_scale) / (n/2);
visu.bars[pos].limit = min + step;
@@ -957,6 +983,29 @@ void spectrum_limits(int min, int n, int pos) {
}
}
/****************************************************************************************
* Fit visu
*/
static void visu_fit(int bars, int width, int height) {
// try to adapt to what we have
if ((visu.mode & ~VISU_ESP32) == VISU_SPECTRUM) {
visu.n = bars ? bars : MAX_BARS;
visu.max = height - 1;
if (visu.spectrum_scale <= 0 || visu.spectrum_scale > 0.5) visu.spectrum_scale = 0.5;
spectrum_limits(0, visu.n, 0);
} else {
visu.n = 2;
visu.max = (visu.style ? VU_COUNT : height) - 1;
}
do {
visu.bar_width = (width - visu.border - visu.bar_gap * (visu.n - 1)) / visu.n;
if (visu.bar_width > 0) break;
} while (--visu.n);
visu.bar_border = (width - visu.border - (visu.bar_width + visu.bar_gap) * visu.n + visu.bar_gap) / 2;
}
/****************************************************************************************
* Visu packet handler
*/
@@ -976,12 +1025,12 @@ 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 >= displayer.height) GDS_ClearExt(display, false, true, visu.col, visu.row, visu.col + visu.width - 1, visu.row + visu.height - 1);
if (visu.mode) {
// these will be overidden if necessary
visu.col = visu.border = 0;
visu.width = displayer.width;
visu.rotate = false;
// what type of visu
if (visu.mode & VISU_ESP32) {
@@ -993,7 +1042,7 @@ static void visu_handler( u8_t *data, int len) {
visu.style = 0;
visu.width = htonl(pkt->width);
visu.height = pkt->height ? pkt->height : SB_HEIGHT;
visu.height = pkt->height ? pkt->height : displayer.height;
visu.col = pkt->col < 0 ? displayer.width + pkt->col : pkt->col;
visu.row = pkt->row < 0 ? GDS_GetHeight(display) + pkt->row : pkt->row;
visu.border = htonl(pkt->border);
@@ -1001,9 +1050,13 @@ static void visu_handler( u8_t *data, int len) {
visu.spectrum_scale = htonl(pkt->spectrum_scale) / 100.;
} else {
// full screen visu, try to use bottom screen if available
visu.height = GDS_GetHeight(display) > SB_HEIGHT ? GDS_GetHeight(display) - SB_HEIGHT : GDS_GetHeight(display);
visu.width = htonl(pkt->full.width);
visu.height = GDS_GetHeight(display) > displayer.height ? GDS_GetHeight(display) - displayer.height : GDS_GetHeight(display);
visu.row = GDS_GetHeight(display) - visu.height;
// try to estimate if we should rotate visu
if (visu.height > displayer.height && visu.height > 2*visu.width) visu.rotate = true;
// is this spectrum or analogue/digital
if ((visu.mode & ~VISU_ESP32) == VISU_SPECTRUM) {
bars = htonl(pkt->full.bars);
@@ -1016,37 +1069,29 @@ static void visu_handler( u8_t *data, int len) {
} else {
// classical (screensaver) mode, don't try to optimize screen usage & force some params
visu.row = 0;
visu.height = SB_HEIGHT;
visu.height = GDS_GetHeight(display);
visu.width = displayer.width;
visu.spectrum_scale = 0.25;
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 (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 (visu.style) visu.row = visu.height - VU_HEIGHT;
}
if (bars > MAX_BARS) bars = MAX_BARS;
}
// try to adapt to what we have
if ((visu.mode & ~VISU_ESP32) == VISU_SPECTRUM) {
visu.n = bars ? bars : MAX_BARS;
visu.max = displayer.height - 1;
if (visu.spectrum_scale <= 0 || visu.spectrum_scale > 0.5) visu.spectrum_scale = 0.5;
spectrum_limits(0, visu.n, 0);
} else {
visu.n = 2;
visu.max = visu.style ? (VU_COUNT - 1) : (displayer.height - 1);
}
do {
visu.bar_width = (visu.width - visu.border - visu.bar_gap * (visu.n - 1)) / visu.n;
if (visu.bar_width > 0) break;
} while (--visu.n);
visu.bar_border = (visu.width - visu.border - (visu.bar_width + visu.bar_gap) * visu.n + visu.bar_gap) / 2;
// for rotate, swap width & height
if (visu.rotate) visu_fit(bars, visu.height, visu.width);
else visu_fit(bars, visu.width, visu.height);
// give up if not enough space
if (visu.bar_width < 0) {
visu.mode = VISU_BLANK;
LOG_WARN("Not enough room for displaying visu");
} else {
// de-activate scroller if we are taking main screen
if (visu.row < SB_HEIGHT) scroller.active = false;
if (visu.row < displayer.height) scroller.active = false;
vTaskResume(displayer.task);
}
visu.wake = 0;

View File

@@ -65,11 +65,11 @@ void decode_restore(int external);
// to be defined to nothing if you don't want to support these
extern struct visu_export_s {
pthread_mutex_t mutex;
u32_t level, size, rate;
u32_t level, size, rate, gain;
s16_t *buffer;
bool running;
} visu_export;
void output_visu_export(s16_t *frames, frames_t out_frames, u32_t rate, bool silence);
void output_visu_export(s16_t *frames, frames_t out_frames, u32_t rate, bool silence, u32_t gain);
void output_visu_init(log_level level);
void output_visu_close(void);

View File

@@ -122,7 +122,7 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g
memcpy(btout + oframes * BYTES_PER_FRAME, buf, out_frames * BYTES_PER_FRAME);
}
output_visu_export((s16_t*) (btout + oframes * BYTES_PER_FRAME), out_frames, output.current_sample_rate, silence);
output_visu_export((s16_t*) (btout + oframes * BYTES_PER_FRAME), out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
return (int)out_frames;
}

View File

@@ -367,7 +367,7 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
_scale_and_pack_frames(obuf + oframes * bytes_per_frame, optr, out_frames, gainL, gainR, output.format);
#endif
output_visu_export((s16_t*) (obuf + oframes * bytes_per_frame), out_frames, output.current_sample_rate, silence);
output_visu_export((s16_t*) (obuf + oframes * bytes_per_frame), out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
oframes += out_frames;

View File

@@ -29,7 +29,7 @@ static struct visu_export_s *visu = &visu_export;
static log_level loglevel = lINFO;
void output_visu_export(s16_t *frames, frames_t out_frames, u32_t rate, bool silence) {
void output_visu_export(s16_t *frames, frames_t out_frames, u32_t rate, bool silence, u32_t gain) {
// no data to process
if (silence) {
@@ -50,6 +50,7 @@ void output_visu_export(s16_t *frames, frames_t out_frames, u32_t rate, bool sil
visu->level += space / 2;
visu->running = true;
visu->rate = rate ? rate : 44100;
visu->gain = gain;
}
// mutex must be released

Binary file not shown.

View File

@@ -77,7 +77,7 @@ sub displayWidth {
if ($display->widthOverride) {
my $artwork = $prefs->client($client)->get('artwork');
if ($artwork->{'enable'} && $artwork->{'y'} < 32 && $client->isPlaying) {
if ($artwork->{'enable'} && $artwork->{'y'} < 32 && ($client->isPlaying || $client->isPaused)) {
return $artwork->{x} + ($display->modes->[$mode || 0]{_width} || 0);
} else {
return $display->widthOverride + ($display->modes->[$mode || 0]{_width} || 0);
@@ -110,12 +110,13 @@ sub build_modes {
my $client = shift->client;
my $cprefs = $prefs->client($client);
my $width = $cprefs->get('width') || 128;
my $artwork = $cprefs->get('artwork');
my $disp_width = $cprefs->get('width') || 128;
# if artwork is in main display, reduce width
$width = $artwork->{'x'} if $artwork->{'enable'} && $artwork->{y} < 32;
my $width = ($artwork->{'enable'} && $artwork->{'y'} < 32) ? $artwork->{'x'} : $disp_width;
my $width_low = ($artwork->{'enable'} && ($artwork->{'y'} >= 32 || $disp_width - $artwork->{'x'} > 32)) ? $artwork->{'x'} : $disp_width;
my $small_VU = $cprefs->get('small_VU');
my $spectrum = $cprefs->get('spectrum');
@@ -168,45 +169,58 @@ sub build_modes {
# mode 9
{ desc => ['VISUALIZER_VUMETER'],
bar => 0, secs => 0, width => $width,
params => [$VISUALIZER_VUMETER_ESP32, 0] },
params => [$VISUALIZER_VUMETER_ESP32, $width_low, 0] },
# mode 10
{ desc => ['VISUALIZER_ANALOG_VUMETER'],
bar => 0, secs => 0, width => $width,
params => [$VISUALIZER_VUMETER_ESP32, 1] },
params => [$VISUALIZER_VUMETER_ESP32, $width_low, 1] },
# mode 11
{ desc => ['VISUALIZER_SPECTRUM_ANALYZER'],
bar => 0, secs => 0, width => $width,
# extra parameters (bars)
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, $width_low, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
);
my @extra = (
# mode E1
{ desc => ['VISUALIZER_VUMETER', 'AND', 'ELAPSED'],
bar => 0, secs => 1, width => $width,
params => [$VISUALIZER_VUMETER_ESP32, 0] },
params => [$VISUALIZER_VUMETER_ESP32, $width_low, 0] },
# mode E2
{ desc => ['VISUALIZER_ANALOG_VUMETER', 'AND', 'ELAPSED'],
bar => 0, secs => 1, width => $width,
params => [$VISUALIZER_VUMETER_ESP32, 1] },
params => [$VISUALIZER_VUMETER_ESP32, $width_low, 1] },
# mode E3
{ desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'ELAPSED'],
bar => 0, secs => 1, width => $width,
# extra parameters (bars)
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, $width_low, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
# mode E4
{ desc => ['VISUALIZER_VUMETER', 'AND', 'REMAINING'],
bar => 0, secs => -1, width => $width,
params => [$VISUALIZER_VUMETER_ESP32, 0] },
params => [$VISUALIZER_VUMETER_ESP32, $width_low, 0] },
# mode E5
{ desc => ['VISUALIZER_ANALOG_VUMETER', 'AND', 'REMAINING'],
bar => 0, secs => -1, width => $width,
params => [$VISUALIZER_VUMETER_ESP32, 1] },
params => [$VISUALIZER_VUMETER_ESP32, $width_low, 1] },
# mode E6
{ desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'REMAINING'],
bar => 0, secs => -1, width => $width,
# extra parameters (bars)
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, $width_low, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
# mode E7
{ desc => ['VISUALIZER_VUMETER', 'AND', 'PROGRESS_BAR', 'AND', 'REMAINING'],
bar => 1, secs => -1, width => $width,
params => [$VISUALIZER_VUMETER_ESP32, $width_low, 0] },
# mode E8
{ desc => ['VISUALIZER_ANALOG_VUMETER', 'AND', 'PROGRESS_BAR', 'AND', 'REMAINING'],
bar => 1, secs => -1, width => $width,
params => [$VISUALIZER_VUMETER_ESP32, $width_low, 1] },
# mode E9
{ desc => ['VISUALIZER_SPECTRUM_ANALYZER', 'AND', 'PROGRESS_BAR', 'AND', 'REMAINING'],
bar => 1, secs => -1, width => $width,
# extra parameters (bars)
params => [$VISUALIZER_SPECTRUM_ANALYZER_ESP32, $width_low, int ($width/$spectrum->{full}->{band}), $spectrum->{scale}] },
);
@modes = (@modes, @extra) if $cprefs->get('height') > 32;

View File

@@ -35,8 +35,22 @@ sub initPlugin {
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['newmetadata'] ] );
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'newsong'] ]);
Slim::Control::Request::subscribe( \&onStopClear, [ ['playlist'], ['stop', 'clear'] ]);
}
sub onStopClear {
my $request = shift;
my $client = $request->client;
my $artwork = $prefs->client($client)->get('artwork');
if ($client->model eq 'squeezeesp32' && $artwork->{'enable'}) {
my $reqstr = $request->getRequestString();
$log->info("artwork stop/clear $reqstr");
$client->pluginData('artwork_md5', '')
}
}
sub onNotification {
my $request = shift;
my $client = $request->client;
@@ -56,8 +70,7 @@ sub update_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 $s = min($cprefs->get('height') - $artwork->{'y'}, $cprefs->get('width') - $artwork->{'x'});
my $path = 'music/current/cover_' . $s . 'x' . $s . '_o.jpg';
my $body = Slim::Web::Graphics::artworkRequest($client, $path, $params, \&send_artwork, undef, HTTP::Response->new);

View File

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

View File

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