mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-07 20:17:04 +03:00
Compare commits
29 Commits
v0.5.663-v
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d45fb6f8f | ||
|
|
d76001a023 | ||
|
|
7951c59d11 | ||
|
|
64d609cc33 | ||
|
|
6b368a7c02 | ||
|
|
3159edc26c | ||
|
|
86cee80393 | ||
|
|
5e11ccbd87 | ||
|
|
874abb99ed | ||
|
|
3c418097b8 | ||
|
|
c576587de4 | ||
|
|
f06c08cef7 | ||
|
|
6385fe9be4 | ||
|
|
31cb0f5df4 | ||
|
|
2df24e2463 | ||
|
|
c844ce01e4 | ||
|
|
a883bd97ef | ||
|
|
b7932a6407 | ||
|
|
8e8a1ffe3d | ||
|
|
a35b5204d5 | ||
|
|
ee7d2a492c | ||
|
|
46f89026ed | ||
|
|
3c317b0b86 | ||
|
|
8b97e3562d | ||
|
|
57ae93447c | ||
|
|
29a74b9ca8 | ||
|
|
d95d1cd3db | ||
|
|
8947f2fc75 | ||
|
|
b6c296460c |
30
.github/workflows/I2S-4MBFlash.yml
vendored
Normal file
30
.github/workflows/I2S-4MBFlash.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: I2S-4MBFlash
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
on:
|
||||
push:
|
||||
branches: [ master-cmake ]
|
||||
pull_request:
|
||||
branches: [ master-cmake ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Cache build
|
||||
id: cache-build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{github.workspace}}/build
|
||||
key: ${{ runner.os }}-I2S-4MBFlash
|
||||
- name: Build the firmware
|
||||
run: |
|
||||
docker run --rm -v $PWD:/project -w /project espressif/idf:release-v4.0 /bin/bash -c "python3 -m pip install --upgrade pip setuptools wheel && pip3 install protobuf grpcio-tools && cp build-scripts/I2S-4MFlash-sdkconfig.defaults sdkconfig && idf.py build"
|
||||
|
||||
|
||||
20
.github/workflows/cmake.yml
vendored
Normal file
20
.github/workflows/cmake.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Test-workflow
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set Target
|
||||
run: echo "TARGET_BUILD_NAME=SqueezeAmp" >> $GITHUB_ENV
|
||||
- name: Get Target
|
||||
run: |
|
||||
echo "Target is ${TARGET_BUILD_NAME}"
|
||||
env
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -67,3 +67,4 @@ libs/
|
||||
/_*
|
||||
sdkconfig
|
||||
squeezelite-esp32-jsonblob.zip
|
||||
*.bak
|
||||
|
||||
76
README.md
76
README.md
@@ -1,11 +1,41 @@
|
||||
# Squeezelite-esp32
|
||||
## Supported Hardware
|
||||
## What is this
|
||||
Squeezelite-esp32 is an audio software suite made to run on espressif's ESP32 wifi (b/g/n) and bluetooth chipset. It offers the following capabilities
|
||||
|
||||
- Stream your local music and connect to all major on-line music providers (Spotify, Deezer, Tidal, Qobuz) using [Logitech Media Server - a.k.a LMS](https://forums.slimdevices.com/) and enjoy multi-room audio synchronization. LMS can be extended by numerous plugins and can be controlled using a Web browser or dedicated applications (iPhone, Android). It can also send audio to UPnP, Sonos, ChromeCast and AirPlay speakers/devices.
|
||||
- Stream from a Bluetooth device (iPhone, Android)
|
||||
- Stream from an AirPlay controller (iPhone, iTunes ...) and enjoy synchronization multiroom as well (although it's AirPlay 1 only)
|
||||
|
||||
Depending on the hardware connected to the ESP32, you can send audio to a local DAC, to SPDIF or to a Bluetooth speaker. The bare minimum required hardware is a WROVER module with 4MB of Flash and 4MB of PSRAM (https://www.espressif.com/en/products/modules/esp32). With that module standalone, just apply power and you can stream to a Bluetooth speaker. You can also send audio to most I2S DAC as well as to SPDIF receivers using just a cable or an optical transducer (you'll need *one* resistor...)
|
||||
|
||||
But squeezelite-esp32 is highly extensible and you can add
|
||||
|
||||
- Buttons and Rotary Encoder and map/combine them to various functions (play, pause, volume, next ...)
|
||||
- IR receiver (no pullup resistor or capacitor needed, just the 38kHz receiver)
|
||||
- Monochrome, GrayScale or Color displays using SPI or I2S (supported drivers are SH1106, SSD1306, SSD1322, SSD1326/7, SSD1351, ST7735 and ST7789).
|
||||
|
||||
Other features include
|
||||
|
||||
- Resampling
|
||||
- 10-bands equalizer
|
||||
- Automatic initial setup using any WiFi device
|
||||
- Full web interface for further configuration/management
|
||||
- Firmware over-the-air update
|
||||
|
||||
## Supported Hardware
|
||||
Any esp32-based hardware with at least 4MB of flash and 4MB of PSRAM will be capable of running squeezelite-esp32 and there are various boards that include such chip. A few are mentionned below, but any should work.
|
||||
### Raw WROVER module
|
||||
Per above description, a [WROVER module](https://www.espressif.com/en/products/modules/esp32) is enough to run Squeezelite-esp32, but that requires a bit of tinkering to extend it to have analogue audio or hardware buttons (e.g.)
|
||||
|
||||
Please note that when sending to a Bluetooth speaker, then resampling *must* be enabled (using -R) option if you want to send audio with rate other than 44.1kHz. Similarly, when using SPDIF, only 44.1kHz and 48kHz are supported so you might have to enable resampling as well. If you connect a DAC, choice will depends on its capabilities. See below for more details.
|
||||
|
||||
Most DAC will work out-of-the-box with simply an I2S connection, but some require specific commands to be sent using I2C. See DAC option below to understand how to send these dedicated commands. There is build-in support for TAS575x, TAS5780, TAS5713 and AC101 DAC.
|
||||
### SqueezeAMP
|
||||
Works with the SqueezeAMP see [here](https://forums.slimdevices.com/showthread.php?110926-pre-ANNOUNCE-SqueezeAMP-and-SqueezeliteESP32) and [here](https://github.com/philippe44/SqueezeAMP).
|
||||
This is the main hardware companion of Squeezelite-esp32 and has been developped together. Details on capabilities can be found [here](https://forums.slimdevices.com/showthread.php?110926-pre-ANNOUNCE-SqueezeAMP-and-SqueezeliteESP32) and [here](https://github.com/philippe44/SqueezeAMP).
|
||||
|
||||
if you want to rebuild, use the `squeezelite-esp32-SqueezeAmp-sdkconfig.defaults` configuration file.
|
||||
|
||||
NB: You can use the pre-build binaries SqueezeAMP4MBFlash/SqueezeAMP8MBFlash which has all the hardware I/O set properly. You can also use the generic binary I2S4MBFlash in which case the NVS parameters shall be set to get the exact same behavior
|
||||
NB: You can use the pre-build binaries SqueezeAMP4MBFlash which has all the hardware I/O set properly. You can also use the generic binary I2S4MBFlash in which case the NVS parameters shall be set to get the exact same behavior
|
||||
- set_GPIO: 12=green,13=red,34=jack,2=spkfault
|
||||
- batt_config: channel=7,scale=20.24
|
||||
- dac_config: model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0
|
||||
@@ -84,7 +114,7 @@ The parameter "dac_controlset" allows definition of simple commands to be sent o
|
||||
poweron: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
|
||||
poweroff: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ] }
|
||||
```
|
||||
This is standard JSON notation, so if you are not familiar with it, Google is your best friend. Be aware that the '...' means you can have as many entries as you want, it's not part of the syntax. Every section is optional, but it does not make sense to set i2c in the 'dac_config' parameter and not setting anything here. The parameter 'mode' allows to *or* the register with the value or to *and* it. Don't set 'mode' if you simply want to write. **Note that all values must be decimal**
|
||||
This is standard JSON notation, so if you are not familiar with it, Google is your best friend. Be aware that the '...' means you can have as many entries as you want, it's not part of the syntax. Every section is optional, but it does not make sense to set i2c in the 'dac_config' parameter and not setting anything here. The parameter 'mode' allows to *or* the register with the value or to *and* it. Don't set 'mode' if you simply want to write. **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax
|
||||
|
||||
NB: For well-known configuration, this is ignored
|
||||
### SPDIF
|
||||
@@ -102,10 +132,11 @@ NB: For well-known configuration, this is ignored
|
||||
### Display
|
||||
The NVS parameter "display_config" sets the parameters for an optional display. Syntax is
|
||||
```
|
||||
I2C,width=<pixels>,height=<pixels>[address=<i2c_address>][,HFlip][,VFlip][driver=SSD1306|SSD1326[:1|4]|SSD1327|SH1106]
|
||||
SPI,width=<pixels>,height=<pixels>,cs=<gpio>[,back=<gpio>][,speed=<speed>][,HFlip][,VFlip][driver=SSD1306|SSD1322|SSD1326[:1|4]|SSD1327|SH1106|SSD1675|ST7735|ST7789[,rotate]]
|
||||
I2C,width=<pixels>,height=<pixels>[address=<i2c_address>][,reset=<gpio>][,HFlip][,VFlip][driver=SSD1306|SSD1326[:1|4]|SSD1327|SH1106]
|
||||
SPI,width=<pixels>,height=<pixels>,cs=<gpio>[,back=<gpio>][,reset=<gpio>][,speed=<speed>][,HFlip][,VFlip][driver=SSD1306|SSD1322|SSD1326[:1|4]|SSD1327|SH1106|SSD1675|ST7735|ST7789[,rotate]]
|
||||
```
|
||||
- back: a LED backlight used by some older devices (ST7735). It is PWM controlled for brightness
|
||||
- reset: some display have a reset pin that is should normally be pulled up if unused
|
||||
- VFlip and HFlip are optional can be used to change display orientation
|
||||
- rotate: for non-square *drivers*, move to portrait mode. Note that *width* and *height* must be inverted then
|
||||
- Default speed is 8000000 (8MHz) but SPI can work up to 26MHz or even 40MHz
|
||||
@@ -363,7 +394,7 @@ You also need to use esp-dsp recent version or at least make sure you have this
|
||||
|
||||
## Building Squeezelite-esp32
|
||||
Don't forget the to choose one of the config files in build_scripts/ and rename it sdkconfig.defaults or sdkconfig as many important WiFi/BT options are set there. The codecs libraries will not be rebuilt by these scripts (it's a tedious process - see below)
|
||||
### Usng make (deprecated)
|
||||
### Using make (deprecated)
|
||||
MOST IMPORTANT: create the right default config file
|
||||
- make defconfig
|
||||
(Note: You can also copy over config files from the build-scripts folder to ./sdkconfig)
|
||||
@@ -401,34 +432,33 @@ python.exe <idf_path>\components\esptool_py\esptool\esptool.py -p COM<n> -b 9216
|
||||
```
|
||||
Use 'idf monitor' to monitor the application (see esp-idf documentation)
|
||||
## Additional misc notes to do you build (kitchen sink)
|
||||
- don't forget to set IDF_PATH, ESPPORT and ESPBAUD
|
||||
- When initially cloning the repo, make sure you do it recursively. For example:
|
||||
- git clone --recursive https://github.com/sle118/squeezelite-esp32.git
|
||||
- If you have already cloned the repository and you are getting compile errors on one of the submodules (e.g. telnet), run the following git command in the root of the repository location
|
||||
- git submodule update --init --recursive
|
||||
- as of this writing, ESP-IDF has a bug int he way the PLL values are calculated for i2s, so you *must* use the i2s.c file in the patch directory
|
||||
- misc compiler #define
|
||||
- use no resampling or set RESAMPLE (soxr - but overloads CPU) or set RESAMPLE16 for fast fixed 16 bits resampling
|
||||
- use LOOPBACK (mandatory)
|
||||
- use BYTES_PER_FRAME=4 (8 is not fully functionnal)
|
||||
- LINKALL (mandatory)
|
||||
- NO_FAAD unless you want to us faad, which currently overloads the CPU
|
||||
- TREMOR_ONLY (mandatory)
|
||||
### codecs
|
||||
- for codecs libraries, add -mlongcalls if you want to rebuild them, but you should not (use the provided ones in codecs/lib). if you really want to rebuild them, open an issue
|
||||
- libmad, libflac (no esp's version), libvorbis (tremor - not esp's version), alac work
|
||||
- libfaad does not really support real time, but if you want to try
|
||||
- libfaad does not really support real time, but if you want to try (but using helixaac is a better option)
|
||||
- -O3 -DFIXED_POINT -DSMALL_STACK
|
||||
- change ac_link in configure and case ac_files, remove ''
|
||||
- compiler but in cfft.c and cffti1, must disable optimization using
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
#pragma GCC pop_options
|
||||
- better use helixaac
|
||||
- opus & opusfile
|
||||
- for opus, the ESP-provided library seems to work, but opusfile is still needed
|
||||
- per mad & few others, edit configure and change $ac_link to add -c (faking link)
|
||||
- change ac_files to remove ''
|
||||
- add DEPS_CFLAGS and DEPS_LIBS to avoid pkg-config to be required
|
||||
- stack consumption can be very high with some codec variants, so set NONTHREADSAFE_PSEUDOSTACK and GLOBAL_STACK_SIZE=32000 and unset VAR_ARRAYS in config.h
|
||||
- set IDF_PATH=/home/esp-idf
|
||||
- other compiler #define
|
||||
- use no resampling or set RESAMPLE (soxr) or set RESAMPLE16 for fast fixed 16 bits resampling
|
||||
- use LOOPBACK (mandatory)
|
||||
- use BYTES_PER_FRAME=4 (8 is not fully functionnal)
|
||||
- LINKALL (mandatory)
|
||||
- NO_FAAD unless you want to us faad, which currently overloads the CPU
|
||||
- TREMOR_ONLY (mandatory)
|
||||
- better use helixaac
|
||||
- libmad has been patched to avoid using a lot of stack. There is an issue with sycn detection in 1.15.1b from where the original stack patch was done but since a few fixes have been made wrt sync detection. This 1.15.1b-10 found on debian fixes the issue where mad thinks it has reached sync but has not and so returns a wrong sample rate. It comes at the expense of 8KB (!) of code where a simple check in squeezelite/mad.c that next_frame[0] is 0xff and next_frame[1] & 0xf0 is 0xf0 does the trick ...
|
||||
- When initially cloning the repo, make sure you do it recursively. For example:
|
||||
- git clone --recursive https://github.com/sle118/squeezelite-esp32.git
|
||||
- If you have already cloned the repository and you are getting compile errors on one of the submodules (e.g. telnet), run the following git command in the root of the repository location
|
||||
- git submodule update --init --recursive
|
||||
- libmad has been patched to avoid using a lot of stack and is not provided here. There is an issue with sync detection in 1.15.1b from where the original stack patch was done but since a few fixes have been made wrt sync detection. This 1.15.1b-10 found on debian fixes the issue where mad thinks it has reached sync but has not and so returns a wrong sample rate. It comes at the expense of 8KB (!) of code where a simple check in squeezelite/mad.c that next_frame[0] is 0xff and next_frame[1] & 0xf0 is 0xf0 does the trick ...
|
||||
|
||||
@@ -35,7 +35,7 @@ static bool I2CDefaultWriteData( struct GDS_Device* Device, const uint8_t* Data,
|
||||
bool GDS_I2CInit( int PortNumber, int SDA, int SCL, int Speed ) {
|
||||
I2CPortNumber = PortNumber;
|
||||
|
||||
I2CWait = pdMS_TO_TICKS( Speed ? Speed / 4000 : 100 );
|
||||
I2CWait = pdMS_TO_TICKS( Speed ? (250 * 250000) / Speed : 250 );
|
||||
|
||||
if (SDA != -1 && SCL != -1) {
|
||||
i2c_config_t Config = { 0 };
|
||||
|
||||
10
components/squeezelite/external/dac_external.c
vendored
10
components/squeezelite/external/dac_external.c
vendored
@@ -128,12 +128,12 @@ static esp_err_t i2c_write_reg(uint8_t reg, uint8_t val) {
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
|
||||
i2c_master_write_byte(cmd, i2c_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
|
||||
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
|
||||
i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
|
||||
i2c_master_write_byte(cmd, val, I2C_MASTER_NACK);
|
||||
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
@@ -153,15 +153,15 @@ static uint8_t i2c_read_reg(uint8_t reg) {
|
||||
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
|
||||
i2c_master_start(cmd);
|
||||
|
||||
i2c_master_write_byte(cmd, i2c_addr | I2C_MASTER_WRITE, I2C_MASTER_NACK);
|
||||
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
|
||||
i2c_master_write_byte(cmd, reg, I2C_MASTER_NACK);
|
||||
|
||||
i2c_master_start(cmd);
|
||||
i2c_master_write_byte(cmd, i2c_addr | I2C_MASTER_READ, I2C_MASTER_NACK);
|
||||
i2c_master_write_byte(cmd, (i2c_addr << 1) | I2C_MASTER_READ, I2C_MASTER_NACK);
|
||||
i2c_master_read_byte(cmd, &data, I2C_MASTER_NACK);
|
||||
|
||||
i2c_master_stop(cmd);
|
||||
ret = i2c_master_cmd_begin(i2c_port, cmd, 1000 / portTICK_RATE_MS);
|
||||
ret = i2c_master_cmd_begin(i2c_port, cmd, 100 / portTICK_RATE_MS);
|
||||
i2c_cmd_link_delete(cmd);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
|
||||
@@ -44,7 +44,7 @@ menu "Squeezelite-ESP32"
|
||||
string
|
||||
default "model=TAS57xx,bck=33,ws=25,do=32,sda=27,scl=26,mute=14:0" if SQUEEZEAMP
|
||||
default "model=AC101,bck=27,ws=26,do=25,di=35,sda=33,scl=32" if A1S
|
||||
default "model=I2S,bck=26,ws=25,do=33,i2c=106,sda=21,scl=22" if TWATCH2020
|
||||
default "model=I2S,bck=26,ws=25,do=33,i2c=53,sda=21,scl=22" if TWATCH2020
|
||||
default ""
|
||||
config SPDIF_CONFIG
|
||||
string
|
||||
|
||||
@@ -413,7 +413,7 @@ void app_main()
|
||||
|
||||
/* start the wifi manager */
|
||||
ESP_LOGD(TAG,"Blinking led");
|
||||
led_blink(LED_GREEN, 250, 250);
|
||||
led_blink_pushed(LED_GREEN, 250, 250);
|
||||
|
||||
if(bypass_wifi_manager){
|
||||
ESP_LOGW(TAG,"\n\nwifi manager is disabled. Please use wifi commands to connect to your wifi access point.\n\n");
|
||||
|
||||
Binary file not shown.
@@ -1,231 +0,0 @@
|
||||
package Plugins::SqueezeESP32::Graphics;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Slim::Display::Squeezebox2);
|
||||
|
||||
use Slim::Utils::Prefs;
|
||||
use Slim::Utils::Log;
|
||||
|
||||
my $prefs = preferences('plugin.squeezeesp32');
|
||||
my $log = logger('plugin.squeezeesp32');
|
||||
|
||||
my $VISUALIZER_NONE = 0;
|
||||
my $VISUALIZER_VUMETER = 1;
|
||||
my $VISUALIZER_SPECTRUM_ANALYZER = 2;
|
||||
my $VISUALIZER_WAVEFORM = 3;
|
||||
my $VISUALIZER_VUMETER_ESP32 = 0x11;
|
||||
my $VISUALIZER_SPECTRUM_ANALYZER_ESP32 = 0x12;
|
||||
|
||||
{
|
||||
#__PACKAGE__->mk_accessor('array', 'modes');
|
||||
__PACKAGE__->mk_accessor('rw', 'modes');
|
||||
__PACKAGE__->mk_accessor('rw', qw(vfdmodel));
|
||||
}
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $client = shift;
|
||||
|
||||
my $display = $class->SUPER::new($client);
|
||||
my $cprefs = $prefs->client($client);
|
||||
|
||||
$cprefs->init( {
|
||||
width => 128,
|
||||
small_VU => 15,
|
||||
spectrum => { scale => 25,
|
||||
small => { size => 25, band => 5.33 },
|
||||
full => { band => 8 },
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
$display->init_accessor(
|
||||
modes => $display->build_modes,
|
||||
# Only seems to matter for screensaver and update to decide font. Not
|
||||
# any value is acceptable, so use Boom value which seems to be best
|
||||
# compromise
|
||||
vfdmodel => 'graphic-160x32',
|
||||
);
|
||||
|
||||
return $display;
|
||||
}
|
||||
|
||||
=comment
|
||||
sub modes {
|
||||
return \@modes;
|
||||
}
|
||||
=cut
|
||||
|
||||
sub nmodes {
|
||||
return scalar($#{shift->modes()});
|
||||
}
|
||||
|
||||
sub displayWidth {
|
||||
my $display = shift;
|
||||
my $client = $display->client;
|
||||
|
||||
# if we're showing the always-on visualizer & the current buttonmode
|
||||
# hasn't overridden, then use the playing display mode to index
|
||||
# into the display width, otherwise, it's fullscreen.
|
||||
my $mode = 0;
|
||||
|
||||
if ( $display->showVisualizer() && !defined($client->modeParam('visu')) ) {
|
||||
my $cprefs = preferences('server')->client($client);
|
||||
$mode = $cprefs->get('playingDisplayModes')->[ $cprefs->get('playingDisplayMode') ];
|
||||
}
|
||||
|
||||
if ($display->widthOverride) {
|
||||
my $artwork = $prefs->client($client)->get('artwork');
|
||||
if ($artwork->{'enable'} && $artwork->{'y'} < 32 && ($client->isPlaying || $client->isPaused)) {
|
||||
return ($artwork->{x} || $display->widthOverride) + ($display->modes->[$mode || 0]{_width} || 0);
|
||||
} else {
|
||||
return $display->widthOverride + ($display->modes->[$mode || 0]{_width} || 0);
|
||||
}
|
||||
} else {
|
||||
return $display->modes->[$mode || 0]{width};
|
||||
}
|
||||
}
|
||||
|
||||
sub brightnessMap {
|
||||
return (0 .. 5);
|
||||
}
|
||||
|
||||
=comment
|
||||
sub bytesPerColumn {
|
||||
return 4;
|
||||
}
|
||||
=cut
|
||||
|
||||
# I don't think LMS renderer handles properly screens other than 32 pixels. It
|
||||
# seems that all we get is a 32 pixel-tall data with anything else padded to 0
|
||||
# i.e. if we try 64 pixels height, bytes 0..3 and 4..7 will contains the same
|
||||
# pattern than the 32 pixels version, where one would have expected bytes 4..7
|
||||
# to be empty
|
||||
sub displayHeight {
|
||||
return 32;
|
||||
}
|
||||
|
||||
sub build_modes {
|
||||
my $client = shift->client;
|
||||
my $cprefs = $prefs->client($client);
|
||||
|
||||
my $artwork = $cprefs->get('artwork');
|
||||
my $disp_width = $cprefs->get('width') || 128;
|
||||
|
||||
# if artwork is in main display, reduce width but when artwork is (0,0) fake it
|
||||
my $width = ($artwork->{'enable'} && $artwork->{'y'} < 32 && $artwork->{'x'}) ? $artwork->{'x'} : $disp_width;
|
||||
my $width_low = ($artwork->{'enable'} && $artwork->{'x'} && ($artwork->{'y'} >= 32 || $disp_width - $artwork->{'x'} > 32)) ? $artwork->{'x'} : $disp_width;
|
||||
|
||||
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'],
|
||||
bar => 0, secs => 0, width => $width,
|
||||
params => [$VISUALIZER_NONE] },
|
||||
# mode 1
|
||||
{ desc => ['PROGRESS_BAR'],
|
||||
bar => 1, secs => 0, width => $width,
|
||||
params => [$VISUALIZER_NONE] },
|
||||
# mode 2
|
||||
{ desc => ['ELAPSED'],
|
||||
bar => 0, secs => 1, width => $width,
|
||||
params => [$VISUALIZER_NONE] },
|
||||
# mode 3
|
||||
{ desc => ['ELAPSED', 'AND', 'PROGRESS_BAR'],
|
||||
bar => 1, secs => 1, width => $width,
|
||||
params => [$VISUALIZER_NONE] },
|
||||
# mode 4
|
||||
{ desc => ['REMAINING'],
|
||||
bar => 0, secs => -1, width => $width,
|
||||
params => [$VISUALIZER_NONE] },
|
||||
# mode 5
|
||||
{ desc => ['CLOCK'],
|
||||
bar => 0, secs => 0, width => $width, clock => 1,
|
||||
params => [$VISUALIZER_NONE] },
|
||||
# mode 6
|
||||
{ desc => ['SETUP_SHOWBUFFERFULLNESS'],
|
||||
bar => 0, secs => 0, width => $width, fullness => 1,
|
||||
params => [$VISUALIZER_NONE] },
|
||||
# mode 7
|
||||
{ desc => ['VISUALIZER_VUMETER_SMALL'],
|
||||
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_ESP32, $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 => -$small_spectrum_pos->{'width'},
|
||||
# extra parameters (width, height, col (< 0 = from right), row (< 0 = from bottom), left_space, #bars, 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
|
||||
{ desc => ['VISUALIZER_VUMETER'],
|
||||
bar => 0, secs => 0, width => $width,
|
||||
params => [$VISUALIZER_VUMETER_ESP32, $width_low, 0] },
|
||||
# mode 10
|
||||
{ desc => ['VISUALIZER_ANALOG_VUMETER'],
|
||||
bar => 0, secs => 0, width => $width,
|
||||
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, $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, $width_low, 0] },
|
||||
# mode E2
|
||||
{ desc => ['VISUALIZER_ANALOG_VUMETER', 'AND', 'ELAPSED'],
|
||||
bar => 0, secs => 1, width => $width,
|
||||
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, $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, $width_low, 0] },
|
||||
# mode E5
|
||||
{ desc => ['VISUALIZER_ANALOG_VUMETER', 'AND', 'REMAINING'],
|
||||
bar => 0, secs => -1, width => $width,
|
||||
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, $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;
|
||||
|
||||
return \@modes;
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,104 +0,0 @@
|
||||
[% PROCESS settings/header.html %]
|
||||
|
||||
[% IF prefs.pref_width %]
|
||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_WIDTH" desc="PLUGIN_SQUEEZEESP32_WIDTH_DESC" %]
|
||||
<!--<input type="text" readonly class="stdedit" name="pref_width" id="width" value="[% prefs.pref_width %]" size="3">-->
|
||||
<input type="hidden" name="pref_width" value="[% prefs.pref_width %]">
|
||||
[% prefs.pref_width %]
|
||||
[% END %]
|
||||
|
||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_SMALL_VU" desc="PLUGIN_SQUEEZEESP32_SMALL_VU_DESC" %]
|
||||
<input type="number" min="10" max= "50" step="5"class="stdedit" name="pref_small_VU" id="small_VU" value="[% prefs.pref_small_VU %]" 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="spectrum_scale" value="[% pref_spectrum.scale %]" size="3">
|
||||
[% END %]
|
||||
|
||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM" desc="PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_DESC" %]
|
||||
[% "PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_SIZE" | string %] 
|
||||
<input type="number" min="10" max= "50" step="5"class="stdedit" name="pref_spectrum_small_size" id="spectrum_small_size" value="[% pref_spectrum.small.size %]" size="3">
|
||||
[% "PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_BAND" | string %] 
|
||||
<input type="text" class="stdedit" name="pref_spectrum_small_band" id="spectrum_small_band" value="[% pref_spectrum.small.band %]" size="3">
|
||||
[% END %]
|
||||
|
||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND" desc="PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND_DESC" %]
|
||||
<input type="text" class="stdedit" name="pref_spectrum_full_band" id="spectrum_full_band" value="[% pref_spectrum.full.band %]" size="3">
|
||||
[% END %]
|
||||
|
||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_ARTWORK" desc="PLUGIN_SQUEEZEESP32_ARTWORK_DESC" %]
|
||||
[% "PLUGIN_SQUEEZEESP32_ARTWORK_ENABLE" | string %] 
|
||||
<input type="checkbox" name="pref_artwork_enable" [% IF pref_artwork.enable %] checked [% END %]>
|
||||
[% "PLUGIN_SQUEEZEESP32_ARTWORK_X" | string %] 
|
||||
<input type="text" class="stdedit" name="pref_artwork_x" id="artwork_x" value="[% pref_artwork.x %]" size="2">
|
||||
[% "PLUGIN_SQUEEZEESP32_ARTWORK_Y" | string %] 
|
||||
<input type="text" class="stdedit" name="pref_artwork_y" id="artwork_y" value="[% pref_artwork.y %]" size="2">
|
||||
[% END %]
|
||||
|
||||
<hr>
|
||||
[% END %]
|
||||
|
||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_EQUALIZER" desc="" %]
|
||||
<div>[% "PLUGIN_SQUEEZEESP32_EQUALIZER_SAVE" | string %]</div>
|
||||
[% END %]
|
||||
|
||||
<script TYPE="text/javascript">
|
||||
if (Ext) {
|
||||
Ext.onReady(function () {
|
||||
new Ext.util.TaskRunner().start({
|
||||
run: checkEq,
|
||||
interval: 1000
|
||||
});
|
||||
});
|
||||
|
||||
function checkEq() {
|
||||
var eqValues = [];
|
||||
this.lastValues = this.lastValues || [];
|
||||
|
||||
for (var x = 0; x < 10; x++) {
|
||||
eqValues[x] = Ext.get('pref_equalizer.' + x).dom.value || 0;
|
||||
}
|
||||
|
||||
if (eqValues.join() != this.lastValues.join()) {
|
||||
this.lastValues = eqValues;
|
||||
SqueezeJS.Controller.request({
|
||||
params: ['[% playerid %]', ['squeezeesp32', 'seteq', eqValues.join()]]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
[% WRAPPER settingSection %]
|
||||
[% WRAPPER settingGroup title='31Hz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.0" id="pref_equalizer.0" value="[% pref_equalizer.0 %]" size="2"">
|
||||
[% END %]
|
||||
[% WRAPPER settingGroup title='62Hz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.1" id="pref_equalizer.1" value="[% pref_equalizer.1 %]" size="2">
|
||||
[% END %]
|
||||
[% WRAPPER settingGroup title='125Hz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.2" id="pref_equalizer.2" value="[% pref_equalizer.2 %]" size="2">
|
||||
[% END %]
|
||||
[% WRAPPER settingGroup title='250Hz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.3" id="pref_equalizer.3" value="[% pref_equalizer.3 %]" size="2">
|
||||
[% END %]
|
||||
[% WRAPPER settingGroup title='500Hz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.4" id="pref_equalizer.4" value="[% pref_equalizer.4 %]" size="2">
|
||||
[% END %]
|
||||
[% WRAPPER settingGroup title='1kHz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.5" id="pref_equalizer.5" value="[% pref_equalizer.5 %]" size="2">
|
||||
[% END %]
|
||||
[% WRAPPER settingGroup title='2kHz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.6" id="pref_equalizer.6" value="[% pref_equalizer.6 %]" size="2">
|
||||
[% END %]
|
||||
[% WRAPPER settingGroup title='4kHz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.7" id="pref_equalizer.7" value="[% pref_equalizer.7 %]" size="2">
|
||||
[% END %]
|
||||
[% WRAPPER settingGroup title='8kHz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.8" id="pref_equalizer.8" value="[% pref_equalizer.8 %]" size="2">
|
||||
[% END %]
|
||||
[% WRAPPER settingGroup title='16kHz' desc="" %]
|
||||
<input type="text" class="stdedit sliderInput_-13_20" name="pref_equalizer.9" id="pref_equalizer.9" value="[% pref_equalizer.9 %]" size="2">
|
||||
[% END %]
|
||||
[% END %]
|
||||
|
||||
[% PROCESS settings/footer.html %]
|
||||
@@ -1,304 +0,0 @@
|
||||
package Plugins::SqueezeESP32::Player;
|
||||
|
||||
use strict;
|
||||
use base qw(Slim::Player::SqueezePlay);
|
||||
|
||||
use Digest::MD5 qw(md5);
|
||||
use List::Util qw(min);
|
||||
|
||||
use Slim::Utils::Log;
|
||||
use Slim::Utils::Prefs;
|
||||
|
||||
my $sprefs = preferences('server');
|
||||
my $prefs = preferences('plugin.squeezeesp32');
|
||||
my $log = logger('plugin.squeezeesp32');
|
||||
|
||||
{
|
||||
__PACKAGE__->mk_accessor('rw', 'tone_update');
|
||||
}
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $client = $class->SUPER::new(@_);
|
||||
$client->init_accessor(
|
||||
tone_update => 0,
|
||||
);
|
||||
return $client;
|
||||
}
|
||||
|
||||
our $defaultPrefs = {
|
||||
'analogOutMode' => 0,
|
||||
'bass' => 0,
|
||||
'treble' => 0,
|
||||
'lineInAlwaysOn' => 0,
|
||||
'lineInLevel' => 50,
|
||||
'menuItem' => [qw(
|
||||
NOW_PLAYING
|
||||
BROWSE_MUSIC
|
||||
RADIO
|
||||
PLUGIN_MY_APPS_MODULE_NAME
|
||||
PLUGIN_APP_GALLERY_MODULE_NAME
|
||||
FAVORITES
|
||||
GLOBAL_SEARCH
|
||||
PLUGIN_LINE_IN
|
||||
PLUGINS
|
||||
SETTINGS
|
||||
SQUEEZENETWORK_CONNECT
|
||||
)],
|
||||
};
|
||||
|
||||
my $handlersAdded;
|
||||
|
||||
sub model { 'squeezeesp32' }
|
||||
sub modelName { 'SqueezeESP32' }
|
||||
|
||||
sub hasScrolling { 1 }
|
||||
sub hasIR { 1 }
|
||||
# TODO: add in settings when ready
|
||||
sub hasLineIn { 0 }
|
||||
sub hasHeadSubOut { 1 }
|
||||
sub maxTreble { 20 }
|
||||
sub minTreble { -13 }
|
||||
sub maxBass { 20 }
|
||||
sub minBass { -13 }
|
||||
|
||||
sub init {
|
||||
my $client = shift;
|
||||
|
||||
if (!$handlersAdded) {
|
||||
|
||||
# Add a handler for line-in/out status changes
|
||||
Slim::Networking::Slimproto::addHandler( LIOS => \&lineInOutStatus );
|
||||
|
||||
# Create a new event for sending LIOS updates
|
||||
Slim::Control::Request::addDispatch(
|
||||
['lios', '_state'],
|
||||
[1, 0, 0, undef],
|
||||
);
|
||||
|
||||
Slim::Control::Request::addDispatch(
|
||||
['lios', 'linein', '_state'],
|
||||
[1, 0, 0, undef],
|
||||
);
|
||||
|
||||
Slim::Control::Request::addDispatch(
|
||||
['lios', 'lineout', '_state'],
|
||||
[1, 0, 0, undef],
|
||||
);
|
||||
|
||||
$handlersAdded = 1;
|
||||
|
||||
}
|
||||
|
||||
$client->SUPER::init(@_);
|
||||
$client->config_artwork;
|
||||
}
|
||||
|
||||
sub initPrefs {
|
||||
my $client = shift;
|
||||
$sprefs->client($client)->init($defaultPrefs);
|
||||
$client->SUPER::initPrefs;
|
||||
}
|
||||
|
||||
# Allow the player to define it's display width (and probably more)
|
||||
sub playerSettingsFrame {
|
||||
my $client = shift;
|
||||
my $data_ref = shift;
|
||||
|
||||
my $value;
|
||||
my $id = unpack('C', $$data_ref);
|
||||
|
||||
# New SETD command 0xfe for display width & height
|
||||
if ($id == 0xfe) {
|
||||
$value = (unpack('Cn', $$data_ref))[1];
|
||||
if ($value > 100 && $value < 400) {
|
||||
$prefs->client($client)->set('width', $value);
|
||||
|
||||
my $height = (unpack('Cnn', $$data_ref))[2];
|
||||
$prefs->client($client)->set('height', $height || 0);
|
||||
|
||||
$client->display->modes($client->display->build_modes);
|
||||
$client->display->widthOverride(1, $value);
|
||||
$client->update;
|
||||
|
||||
main::INFOLOG && $log->is_info && $log->info("Setting player $value" . "x" . "$height for ", $client->name);
|
||||
}
|
||||
}
|
||||
|
||||
$client->SUPER::playerSettingsFrame($data_ref);
|
||||
}
|
||||
|
||||
sub bass {
|
||||
my ($client, $new) = @_;
|
||||
my $value = $client->SUPER::bass($new);
|
||||
|
||||
$client->update_equalizer($value, [2, 1, 3]) if defined $new;
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
sub treble {
|
||||
my ($client, $new) = @_;
|
||||
my $value = $client->SUPER::treble($new);
|
||||
|
||||
$client->update_equalizer($value, [8, 9, 7]) if defined $new;
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
sub update_equalizer {
|
||||
my ($client, $value, $index) = @_;
|
||||
return if $client->tone_update;
|
||||
|
||||
my $equalizer = $prefs->client($client)->get('equalizer');
|
||||
$equalizer->[$index->[0]] = $value;
|
||||
$equalizer->[$index->[1]] = int($value / 2 + 0.5);
|
||||
$equalizer->[$index->[2]] = int($value / 4 + 0.5);
|
||||
$prefs->client($client)->set('equalizer', $equalizer);
|
||||
}
|
||||
|
||||
sub update_tones {
|
||||
my ($client, $equalizer) = @_;
|
||||
|
||||
$client->tone_update(1);
|
||||
$sprefs->client($client)->set('bass', int(($equalizer->[1] * 2 + $equalizer->[2] + $equalizer->[3] * 4) / 7 + 0.5));
|
||||
$sprefs->client($client)->set('treble', int(($equalizer->[7] * 4 + $equalizer->[8] + $equalizer->[9] * 2) / 7 + 0.5));
|
||||
$client->tone_update(0);
|
||||
}
|
||||
|
||||
sub update_artwork {
|
||||
my $client = shift;
|
||||
my $cprefs = $prefs->client($client);
|
||||
|
||||
my $artwork = $cprefs->get('artwork') || return;
|
||||
|
||||
return unless $artwork->{'enable'};
|
||||
|
||||
my $s = min($cprefs->get('height') - $artwork->{'y'}, $cprefs->get('width') - $artwork->{'x'});
|
||||
|
||||
my $params = { force => shift || 0 };
|
||||
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);
|
||||
|
||||
send_artwork($client, undef, \$body) if $body;
|
||||
}
|
||||
|
||||
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 && !$params->{'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 clear_artwork {
|
||||
my ($client, $request) = @_;
|
||||
|
||||
my $artwork = $prefs->client($client)->get('artwork');
|
||||
|
||||
if ($artwork && $artwork->{'enable'}) {
|
||||
main::INFOLOG && $log->is_info && $log->info("artwork stop/clear " . $request->getRequestString());
|
||||
$client->pluginData('artwork_md5', '');
|
||||
# refresh screen and disable artwork when artwork was full screen (hack)
|
||||
if (!$artwork->{'x'} && !$artwork->{'y'}) {
|
||||
$client->sendFrame(grfa => \("\x00"x4)) unless $artwork->{'x'} || $artwork->{'y'};
|
||||
$client->display->update;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub config_artwork {
|
||||
my ($client) = @_;
|
||||
|
||||
if ( my $artwork = $prefs->client($client)->get('artwork') ) {
|
||||
my $header = pack('Nnn', $artwork->{'enable'}, $artwork->{'x'}, $artwork->{'y'});
|
||||
$client->sendFrame( grfa => \$header );
|
||||
}
|
||||
}
|
||||
|
||||
sub reconnect {
|
||||
my $client = shift;
|
||||
$client->pluginData('artwork_md5', '');
|
||||
$client->SUPER::reconnect(@_);
|
||||
}
|
||||
|
||||
# Change the analog output mode between headphone and sub-woofer
|
||||
# If no mode is specified, the value of the client's analogOutMode preference is used.
|
||||
# Otherwise the mode is temporarily changed to the given value without altering the preference.
|
||||
sub setAnalogOutMode {
|
||||
my $client = shift;
|
||||
# 0 = headphone (internal speakers off), 1 = sub out,
|
||||
# 2 = always on (internal speakers on), 3 = always off
|
||||
my $mode = shift;
|
||||
|
||||
if (! defined $mode) {
|
||||
$mode = $sprefs->client($client)->get('analogOutMode');
|
||||
}
|
||||
|
||||
my $data = pack('C', $mode);
|
||||
$client->sendFrame('audo', \$data);
|
||||
}
|
||||
|
||||
# LINE_IN 0x01
|
||||
# LINE_OUT 0x02
|
||||
# HEADPHONE 0x04
|
||||
|
||||
sub lineInConnected {
|
||||
my $state = Slim::Networking::Slimproto::voltage(shift) || return 0;
|
||||
return $state & 0x01 || 0;
|
||||
}
|
||||
|
||||
sub lineOutConnected {
|
||||
my $state = Slim::Networking::Slimproto::voltage(shift) || return 0;
|
||||
return $state & 0x02 || 0;
|
||||
}
|
||||
|
||||
sub lineInOutStatus {
|
||||
my ( $client, $data_ref ) = @_;
|
||||
|
||||
my $state = unpack 'n', $$data_ref;
|
||||
|
||||
my $oldState = {
|
||||
in => $client->lineInConnected(),
|
||||
out => $client->lineOutConnected(),
|
||||
};
|
||||
|
||||
Slim::Networking::Slimproto::voltage( $client, $state );
|
||||
|
||||
Slim::Control::Request::notifyFromArray( $client, [ 'lios', $state ] );
|
||||
|
||||
if ($oldState->{in} != $client->lineInConnected()) {
|
||||
Slim::Control::Request::notifyFromArray( $client, [ 'lios', 'linein', $client->lineInConnected() ] );
|
||||
if ( Slim::Utils::PluginManager->isEnabled('Slim::Plugin::LineIn::Plugin')) {
|
||||
Slim::Plugin::LineIn::Plugin::lineInItem($client, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oldState->{out} != $client->lineOutConnected()) {
|
||||
Slim::Control::Request::notifyFromArray( $client, [ 'lios', 'lineout', $client->lineOutConnected() ] );
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,91 +0,0 @@
|
||||
package Plugins::SqueezeESP32::PlayerSettings;
|
||||
|
||||
use strict;
|
||||
use base qw(Slim::Web::Settings);
|
||||
use List::Util qw(first);
|
||||
|
||||
use Slim::Utils::Log;
|
||||
use Slim::Utils::Prefs;
|
||||
|
||||
my $sprefs = preferences('server');
|
||||
my $prefs = preferences('plugin.squeezeesp32');
|
||||
my $log = logger('plugin.squeezeesp32');
|
||||
|
||||
sub name {
|
||||
return Slim::Web::HTTP::CSRF->protectName('PLUGIN_SQUEEZEESP32_PLAYERSETTINGS');
|
||||
}
|
||||
|
||||
sub needsClient {
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub validFor {
|
||||
my ($class, $client) = @_;
|
||||
return $client->model eq 'squeezeesp32';
|
||||
}
|
||||
|
||||
sub page {
|
||||
return Slim::Web::HTTP::CSRF->protectURI('plugins/SqueezeESP32/settings/player.html');
|
||||
}
|
||||
|
||||
sub prefs {
|
||||
my ($class, $client) = @_;
|
||||
my @prefs;
|
||||
push @prefs, qw(width small_VU) if defined $client->displayWidth;
|
||||
return ($prefs->client($client), @prefs);
|
||||
}
|
||||
|
||||
sub handler {
|
||||
my ($class, $client, $paramRef) = @_;
|
||||
|
||||
my ($cprefs, @prefs) = $class->prefs($client);
|
||||
|
||||
if ($paramRef->{'saveSettings'}) {
|
||||
if (defined $client->displayWidth) {
|
||||
$cprefs->set('small_VU', $paramRef->{'pref_small_VU'} || 15);
|
||||
my $spectrum = {
|
||||
scale => $paramRef->{'pref_spectrum_scale'} || 25,
|
||||
small => { size => $paramRef->{'pref_spectrum_small_size'} || 25,
|
||||
band => $paramRef->{'pref_spectrum_small_band'} || 5.33 },
|
||||
full => { band => $paramRef->{'pref_spectrum_full_band'} } || 8,
|
||||
};
|
||||
$cprefs->set('spectrum', $spectrum);
|
||||
|
||||
my $artwork = {
|
||||
enable => $paramRef->{'pref_artwork_enable'},
|
||||
x => $paramRef->{'pref_artwork_x'} || 0,
|
||||
y => $paramRef->{'pref_artwork_y'} || 0,
|
||||
};
|
||||
$cprefs->set('artwork', $artwork);
|
||||
$client->display->modes($client->display->build_modes);
|
||||
$client->display->update;
|
||||
|
||||
# force update or disable artwork
|
||||
if ($artwork->{'enable'}) {
|
||||
$client->update_artwork(1);
|
||||
} else {
|
||||
$client->config_artwork();
|
||||
}
|
||||
}
|
||||
|
||||
my $equalizer = $cprefs->get('equalizer');
|
||||
for my $i (0 .. $#{$equalizer}) {
|
||||
$equalizer->[$i] = $paramRef->{"pref_equalizer.$i"} || 0;
|
||||
}
|
||||
$cprefs->set('equalizer', $equalizer);
|
||||
$client->update_tones($equalizer);
|
||||
}
|
||||
|
||||
if (defined $client->displayWidth) {
|
||||
# the Settings super class can't handle anything but scalar values
|
||||
# we need to populate the $paramRef for the other prefs manually
|
||||
$paramRef->{'pref_spectrum'} = $cprefs->get('spectrum');
|
||||
$paramRef->{'pref_artwork'} = $cprefs->get('artwork');
|
||||
}
|
||||
|
||||
$paramRef->{'pref_equalizer'} = $cprefs->get('equalizer');
|
||||
|
||||
return $class->SUPER::handler($client, $paramRef);
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,121 +0,0 @@
|
||||
package Plugins::SqueezeESP32::Plugin;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Slim::Plugin::Base);
|
||||
|
||||
use Slim::Utils::Prefs;
|
||||
use Slim::Utils::Log;
|
||||
use Slim::Web::ImageProxy;
|
||||
|
||||
my $prefs = preferences('plugin.squeezeesp32');
|
||||
|
||||
my $log = Slim::Utils::Log->addLogCategory({
|
||||
'category' => 'plugin.squeezeesp32',
|
||||
'defaultLevel' => 'INFO',
|
||||
'description' => 'PLUGIN_SQUEEZEESP32',
|
||||
});
|
||||
|
||||
# migrate 'eq' pref, as that's a reserved word and could cause problems in the future
|
||||
$prefs->migrateClient(1, sub {
|
||||
my ($cprefs, $client) = @_;
|
||||
$cprefs->set('equalizer', $cprefs->get('eq'));
|
||||
$cprefs->remove('eq');
|
||||
1;
|
||||
});
|
||||
|
||||
$prefs->setChange(sub {
|
||||
send_equalizer($_[2]);
|
||||
}, 'equalizer');
|
||||
|
||||
sub initPlugin {
|
||||
my $class = shift;
|
||||
|
||||
if ( main::WEBUI ) {
|
||||
require Plugins::SqueezeESP32::PlayerSettings;
|
||||
Plugins::SqueezeESP32::PlayerSettings->new;
|
||||
|
||||
# require Plugins::SqueezeESP32::Settings;
|
||||
# Plugins::SqueezeESP32::Settings->new;
|
||||
}
|
||||
|
||||
$class->SUPER::initPlugin(@_);
|
||||
Slim::Networking::Slimproto::addPlayerClass($class, 100, 'squeezeesp32', { client => 'Plugins::SqueezeESP32::Player', display => 'Plugins::SqueezeESP32::Graphics' });
|
||||
main::INFOLOG && $log->is_info && $log->info("Added class 100 for SqueezeESP32");
|
||||
|
||||
# register a command to set the EQ - without saving the values! Send params as single comma separated list of values
|
||||
Slim::Control::Request::addDispatch(['squeezeesp32', 'seteq', '_eq'], [1, 0, 0, \&setEQ]);
|
||||
|
||||
# Note for some forgetful know-it-all: we need to wrap the callback to make it unique. Otherwise subscriptions would overwrite each other.
|
||||
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['newmetadata'] ] );
|
||||
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'newsong'] ]);
|
||||
Slim::Control::Request::subscribe( \&onStopClear, [ ['playlist'], ['stop', 'clear'] ]);
|
||||
|
||||
# the custom player class is only initialized if it has a display - thus we need to listen to connect events in order to initializes other player prefs
|
||||
Slim::Control::Request::subscribe( \&onPlayer,[ ['client'], [ 'new', 'reconnect' ] ] );
|
||||
}
|
||||
|
||||
sub onStopClear {
|
||||
my $request = shift;
|
||||
my $client = $request->client || return;
|
||||
|
||||
if ($client->isa('Plugins::SqueezeESP32::Player')) {
|
||||
$client->clear_artwork($request);
|
||||
}
|
||||
}
|
||||
|
||||
sub onPlayer {
|
||||
my $request = shift;
|
||||
my $client = $request->client || return;
|
||||
|
||||
if ($client->model eq 'squeezeesp32') {
|
||||
main::INFOLOG && $log->is_info && $log->info("SqueezeESP player connected: " . $client->id);
|
||||
|
||||
$prefs->client($client)->init( {
|
||||
equalizer => [(0) x 10],
|
||||
} );
|
||||
send_equalizer($client);
|
||||
}
|
||||
}
|
||||
|
||||
sub onNotification {
|
||||
my $request = shift;
|
||||
my $client = $request->client || return;
|
||||
|
||||
if ($client->isa('Plugins::SqueezeESP32::Player')) {
|
||||
$client->update_artwork();
|
||||
}
|
||||
}
|
||||
|
||||
sub setEQ {
|
||||
my $request = shift;
|
||||
|
||||
# check this is the correct command.
|
||||
if ($request->isNotCommand([['squeezeesp32'],['seteq']])) {
|
||||
$request->setStatusBadDispatch();
|
||||
return;
|
||||
}
|
||||
|
||||
# get our parameters
|
||||
my $client = $request->client();
|
||||
my @eqParams = split(/,/, $request->getParam('_eq') || '');
|
||||
|
||||
for (my $x = 0; $x < 10; $x++) {
|
||||
$eqParams[$x] ||= 0;
|
||||
}
|
||||
|
||||
send_equalizer($client, \@eqParams);
|
||||
}
|
||||
|
||||
sub send_equalizer {
|
||||
my ($client, $equalizer) = @_;
|
||||
|
||||
if ($client->model eq 'squeezeesp32') {
|
||||
$equalizer ||= $prefs->client($client)->get('equalizer') || [(0) x 10];
|
||||
my $size = @$equalizer;
|
||||
my $data = pack("c[$size]", @{$equalizer});
|
||||
$client->sendFrame( eqlz => \$data );
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,12 +0,0 @@
|
||||
package Plugins::SqueezeESP32::Text;
|
||||
|
||||
use strict;
|
||||
|
||||
use base qw(Slim::Display::Text);
|
||||
|
||||
# we don't want the special Noritake codes
|
||||
sub vfdmodel {
|
||||
return 'squeezeslave';
|
||||
}
|
||||
|
||||
1;
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version='1.0' standalone='yes'?>
|
||||
<extensions>
|
||||
<defaultState>enabled</defaultState>
|
||||
<email>philippe_44@outlook.com</email>
|
||||
<targetApplication>
|
||||
<minVersion>7.9</minVersion>
|
||||
<maxVersion>*</maxVersion>
|
||||
<id>SlimServer</id>
|
||||
</targetApplication>
|
||||
<name>PLUGIN_SQUEEZEESP32</name>
|
||||
<description>PLUGIN_SQUEEZEESP32_DESC</description>
|
||||
<module>Plugins::SqueezeESP32::Plugin</module>
|
||||
<version>0.104</version>
|
||||
<creator>Philippe</creator>
|
||||
</extensions>
|
||||
@@ -1,102 +0,0 @@
|
||||
WELCOME_TO_SQUEEZEESP32
|
||||
DE Willkommen bei SqueezeESP32!
|
||||
EN Welcome to SqueezeESP32
|
||||
|
||||
PLUGIN_SQUEEZEESP32
|
||||
EN SqueezeESP32
|
||||
|
||||
PLUGIN_SQUEEZEESP32_BANNER
|
||||
DE WARNUNG
|
||||
EN WARNING
|
||||
|
||||
PLUGIN_SQUEEZEESP32_BANNER_TEXT
|
||||
DE Sie müssen LMS neu starten, damit diese Einstellungen aktiv werden
|
||||
EN You need to restart LMS for these parameters to be taken into account
|
||||
|
||||
PLUGIN_SQUEEZEESP32_DESC
|
||||
DE Konfiguriert eine neue Player ID (100), um Displays an SqueezeESP32 zu unterstützen
|
||||
EN Adds a new player id (100) to enable display with SqueezeESP32
|
||||
|
||||
PLUGIN_SQUEEZEESP32_PLAYERSETTINGS
|
||||
DE ESP32 Einstellungen
|
||||
EN ESP32 settings
|
||||
|
||||
PLUGIN_SQUEEZEESP32_WIDTH
|
||||
DE Displaybreite
|
||||
EN Screen width
|
||||
|
||||
PLUGIN_SQUEEZEESP32_WIDTH_DESC
|
||||
DE Breite des Displays in Pixeln, wie es vom Player angegeben wird
|
||||
EN Width of the display in pixel as reported by the player
|
||||
|
||||
PLUGIN_SQUEEZEESP32_SMALL_VU
|
||||
DE Kleine VU Grösse
|
||||
EN Small VU size
|
||||
|
||||
PLUGIN_SQUEEZEESP32_SMALL_VU_DESC
|
||||
DE Prozentsatz des Displays, das für den kleinen VU verwendet wird (rechts ausgerichtet)
|
||||
EN % of the display used for small VU (right-justified)
|
||||
|
||||
PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE
|
||||
DE Spektrum-Skalierung
|
||||
EN Spectrum scaling
|
||||
|
||||
PLUGIN_SQUEEZEESP32_SPECTRUM_SCALE_DESC
|
||||
DE Prozentsatz des Spektrums, das in der ersten Hälfte des Bildschirms angezeigt wird. Z.B. 50 bedeutet 50% des Spektrums wird auf dem halben Bildschirm angezeigt.
|
||||
DE Aber 25 bedeutet, dass nur 25% des Spektrums auf dem halben Bildschirm angezeigt wird.
|
||||
EN % of Spectrum displayed in first half of the screen. For example, 50 means that 50% of spectrum is displayed in 1/2 of the screen
|
||||
EN But 25 means that only 25% of spectrum is displayed in 1/2 of the screen, so it's a sort of log
|
||||
|
||||
PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM
|
||||
DE Kleines Spektrum
|
||||
EN Small spectrum options
|
||||
|
||||
PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_DESC
|
||||
DE <i>Grösse</i>: Prozentsatz des Displays, das für das kleine Spektrum verwendet wird.
|
||||
DE <br><i>Band-Faktor</i>: die Anzahl Bänder ist die Breite der <b>Spektrumsanzeige</b> dividiert durch diesen Faktor.
|
||||
EN <i>Size</i>: % of the screen used by small spectrum
|
||||
EN <br><i>Band factor</i>: number of bands is the width of the <b>spectrum</b> screen divided by this factor
|
||||
|
||||
PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_SIZE
|
||||
DE Grösse
|
||||
EN Size
|
||||
|
||||
PLUGIN_SQUEEZEESP32_SMALL_SPECTRUM_BAND
|
||||
DE Band-Faktor
|
||||
EN Band factor
|
||||
|
||||
PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND
|
||||
DE Band-Faktor für ganzes Spektrum
|
||||
EN Full spectrum band factor
|
||||
|
||||
PLUGIN_SQUEEZEESP32_FULL_SPECTRUM_BAND_DESC
|
||||
DE Die Anzahl Bänder ist die Breite der Anzeige dividiert durch diesen Faktor.
|
||||
EN The number of bands is the width of the screen divided by this factor
|
||||
|
||||
PLUGIN_SQUEEZEESP32_ARTWORK
|
||||
DE Plattenhüllen
|
||||
EN Artwork
|
||||
|
||||
PLUGIN_SQUEEZEESP32_ARTWORK_DESC
|
||||
DE Wenn die Y Position kleiner als 32 ist, dann werden Plattenhüllen auf der rechten Seite angezeigt, und x definiert die Startposition.
|
||||
DE Plattenhüllen werden auf Displays mit weniger als 16 Graustufen in sehr geringer Qualität angezeigt.
|
||||
EN When Y position is less than 32, then artwork is displayed 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
|
||||
DE Aktivieren
|
||||
EN Enable
|
||||
|
||||
PLUGIN_SQUEEZEESP32_ARTWORK_X
|
||||
EN X
|
||||
|
||||
PLUGIN_SQUEEZEESP32_ARTWORK_Y
|
||||
EN Y
|
||||
|
||||
PLUGIN_SQUEEZEESP32_EQUALIZER
|
||||
DE Parametrischer Equalizer
|
||||
EN Parametric equalizer
|
||||
|
||||
PLUGIN_SQUEEZEESP32_EQUALIZER_SAVE
|
||||
DE Bitte speichern Sie die Equalizer Einstellungen, falls das Gerät diese dauerhaft verwenden soll. Ansonsten werden sie beim nächsten Start zurückgesetzt.
|
||||
EN Don't forget to save the Equalizer settings if you want them to stick. Otherwise they'll be reset next time you restart the device.
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version='1.0' standalone='yes'?>
|
||||
<extensions>
|
||||
<plugins>
|
||||
<plugin version="0.104" name="SqueezeESP32" minTarget="7.9" maxTarget="*">
|
||||
<plugin version="0.353" name="SqueezeESP32" minTarget="7.9" maxTarget="*">
|
||||
<link>https://github.com/sle118/squeezelite-esp32</link>
|
||||
<creator>Philippe</creator>
|
||||
<sha>79e505a30d7b6dbf43893acab176d57438e2a4a1</sha>
|
||||
<sha>357d715715e7bf10f83ad15bc3fd794fc45e9e5a</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>
|
||||
<desc lang="EN">SqueezeESP32 additional player id (100/101)</desc>
|
||||
<url>http://github.com/sle118/squeezelite-esp32/raw/master-cmake/plugin/SqueezeESP32.zip</url>
|
||||
<title lang="EN">SqueezeESP32</title>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
Reference in New Issue
Block a user