mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-09 13:07:03 +03:00
Compare commits
75 Commits
refactorin
...
I2S-4MFlas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29f71fc677 | ||
|
|
a14a6edc1b | ||
|
|
35bf0a3c10 | ||
|
|
55a8658f3a | ||
|
|
b0d9e1668c | ||
|
|
922367baf5 | ||
|
|
01320db007 | ||
|
|
f677695fc7 | ||
|
|
7902af2bf0 | ||
|
|
5faecb08f6 | ||
|
|
8e3d91020f | ||
|
|
4f54a47c83 | ||
|
|
58840f894f | ||
|
|
94109ebf38 | ||
|
|
fc20618fa2 | ||
|
|
70720d3445 | ||
|
|
4abe1304e8 | ||
|
|
be7fb4b669 | ||
|
|
3554af1460 | ||
|
|
02d422c1f4 | ||
|
|
343b728323 | ||
|
|
9679c5c104 | ||
|
|
5c90086bbd | ||
|
|
807a0e547a | ||
|
|
1d54331224 | ||
|
|
e85a3cddf1 | ||
|
|
d4d97d5a60 | ||
|
|
2afbee7cb1 | ||
|
|
bbf9a3af70 | ||
|
|
a9ca4c4450 | ||
|
|
55bce084eb | ||
|
|
d31697e7ef | ||
|
|
e830b4db73 | ||
|
|
8fe21327b8 | ||
|
|
3f487366ee | ||
|
|
b875585aec | ||
|
|
b46fccfc83 | ||
|
|
655c17fb29 | ||
|
|
b8bda0435a | ||
|
|
dc5cb31efb | ||
|
|
44ccbb49a6 | ||
|
|
6589387cd3 | ||
|
|
0d3f6a8870 | ||
|
|
bb6ec4a629 | ||
|
|
a029304776 | ||
|
|
dad8efff8b | ||
|
|
e8c6169e09 | ||
|
|
fc8d15f58d | ||
|
|
f9d7e15d4b | ||
|
|
67722bef94 | ||
|
|
8ea777ddff | ||
|
|
ad8d587e94 | ||
|
|
667b90fafc | ||
|
|
7be653f722 | ||
|
|
f6269a2f7a | ||
|
|
4b70514fa3 | ||
|
|
00a0b8c36e | ||
|
|
9f4474a19c | ||
|
|
58fac99e95 | ||
|
|
cb7c8fd6be | ||
|
|
b9a1bf0432 | ||
|
|
63658efefe | ||
|
|
c76bbc3524 | ||
|
|
9619b1d792 | ||
|
|
0bbd5a064f | ||
|
|
986521fd4a | ||
|
|
406a56a3a3 | ||
|
|
c9455f70ff | ||
|
|
b5b76480e4 | ||
|
|
e6744deab8 | ||
|
|
94baf86989 | ||
|
|
0c856a37c1 | ||
|
|
d5f28375ce | ||
|
|
7ea5a93647 | ||
|
|
ca38a14420 |
4
.github/workflows/Platform_build.yml
vendored
4
.github/workflows/Platform_build.yml
vendored
@@ -88,7 +88,7 @@ jobs:
|
||||
git commit -m "Update prebuilt objects [skip actions]"
|
||||
git push https://${{secrets.github_token}}@github.com/sle118/squeezelite-esp32.git
|
||||
- name: Locally store commonly built objects
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prebuilt_objects
|
||||
path: |
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
git status
|
||||
build_tools.py environment --build ${{ needs.bootstrap.outputs.build_number }} --env_file "$GITHUB_ENV" --node "${{matrix.node}}" --depth ${{matrix.depth}} --major 2 --docker sle118/squeezelite-esp32-idfv435
|
||||
|
||||
- uses: actions/download-artifact@master
|
||||
- uses: actions/download-artifact@v4
|
||||
name: Restore common objects
|
||||
with:
|
||||
name: prebuilt_objects
|
||||
|
||||
45
CHANGELOG
45
CHANGELOG
@@ -1,3 +1,48 @@
|
||||
2024-01-10
|
||||
- add OggFlac to stream metadata
|
||||
- fix OggFlac deadlock in flac callback when not enough data in streambuf
|
||||
- fix no displayer due to threadshold too high (use 500ms instead)
|
||||
- reset outputbuf when cspot starts
|
||||
|
||||
2024-01-01
|
||||
- ogg stream are parsed to foward metadata to LMS
|
||||
- fix some ogg parsing on multi-stream containers
|
||||
|
||||
2023-11-19
|
||||
- more robust (?) airplay RTP frame recovery
|
||||
- initialize of scratch string in monitor (trying to figure out random reboot)
|
||||
|
||||
2023-11-16
|
||||
- add SH1122 support
|
||||
- optimize GDS DrawPixel function
|
||||
|
||||
2023-11-09
|
||||
- force gpio_pad_select_gpio in dac_controlset in case somebody uses UART gpio's (or other pre-programmed)
|
||||
|
||||
2023-11-08
|
||||
- execute dac_controlset even whne there is no i2s (for gpio)
|
||||
|
||||
2023-11-07
|
||||
- led-vu gain + misc fixes
|
||||
- bump plugin version to 0.600
|
||||
|
||||
2023-11-03
|
||||
- don't reboot when external decoder is connected even with a LMS server
|
||||
|
||||
2023-10-28
|
||||
- fix recovery size (remove bootstrap)
|
||||
- improve NVS initialization structure
|
||||
|
||||
2023-10-27
|
||||
- fix vorbis (and opus) memory leak
|
||||
|
||||
2023-10-25
|
||||
- fix vorbis codec close
|
||||
|
||||
2023-10-23
|
||||
- fix Spotify track insertion
|
||||
- [WEB] Allow running without LMS with option "Audio/Disable Squeezelite"
|
||||
|
||||
2023-10.07
|
||||
- catchup with official cspot
|
||||
|
||||
|
||||
@@ -25,7 +25,11 @@ ENV GCC_TOOLS_BASE=/opt/esp/tools/xtensa-esp32-elf/esp-2021r2-patch3-8.4.0/xtens
|
||||
# pushd components/wifi-manager/webapp/ && npm install && npm run-script build && popd
|
||||
#
|
||||
# to run the docker with netwotrk port published on the host:
|
||||
# (windows)
|
||||
# docker run --rm -p 5000:5000/tcp -v %cd%:/project -w /project -it sle118/squeezelite-esp32-idfv435
|
||||
# (linux)
|
||||
# docker run --rm -p 5000:5000/tcp -v `pwd`:/project -w /project -it sle118/squeezelite-esp32-idfv435
|
||||
|
||||
|
||||
ARG IDF_CLONE_URL=https://github.com/espressif/esp-idf.git
|
||||
ARG IDF_CLONE_BRANCH_OR_TAG=master
|
||||
|
||||
40
README.md
40
README.md
@@ -187,15 +187,23 @@ bck=<gpio>,ws=<gpio>,do=<gpio>[,mck=0|1|2][,mute=<gpio>[:0|1][,model=TAS57xx|TAS
|
||||
```
|
||||
if "model" is not set or is not recognized, then default "I2S" is used. The option "mck" is used for some codecs that require a master clock (although they should not). By default GPIO0 is used as MCLK and only recent builds (post mid-2023) can use 1 or 2. Also be aware that this cannot coexit with RMII Ethernet (see ethernet section below). I2C parameters are optional and only needed if your DAC requires an I2C control (See 'dac_controlset' below). Note that "i2c" parameters are decimal, hex notation is not allowed.
|
||||
|
||||
So far, TAS57xx, TAS5713, AC101, WM8978 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power, speakder and headset on and off using a JSON syntax:
|
||||
So far, TAS57xx, TAS5713, AC101, WM8978 and ES8388 are recognized models where the proper init sequence/volume/power controls are sent. For other codecs that might require an I2C commands, please use the parameter "dac_controlset" that allows definition of simple commands to be sent over i2c for init, power, speaker and headset on and off using a JSON syntax:
|
||||
```json
|
||||
{ <command>: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
|
||||
<command>: [ {"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}, ... {{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"} ],
|
||||
{ <command>: [ <item1>, <item2>, ... <item3> ],
|
||||
<command>: [ <item1>, <item2>, ... <item3> ],
|
||||
... }
|
||||
```
|
||||
Where `<command>` is one of init, poweron, poweroff, speakeron, speakeroff, headseton, headsetoff
|
||||
Where `<command>` is one of init, poweron, poweroff, speakeron, speakeroff, headseton, headsetoff (it **must** be an array even for a single item). Item is any of the following elements
|
||||
```
|
||||
{"reg":<register>,"val":<value>,"mode":<nothing>|"or"|"and"}
|
||||
{"gpio":<gpio>,"level":0|1}
|
||||
{"delay":<ms>}
|
||||
```
|
||||
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.
|
||||
|
||||
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. The 'val parameter can be an array [v1, v2,...] to write a serie of bytes in a single i2c burst (in that case 'mode' is ignored). **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax
|
||||
The `reg` key allow to write registers on i2c bus. 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. The `val` parameter can be an array [v1, v2,...] to write a serie of bytes in a single i2c burst (in that case 'mode' is ignored). **Note that all values must be decimal**. You can use a validator like [this](https://jsonlint.com) to verify your syntax. The `gpio` key is simply to set a gpio as part of DAC action and `delay` allows a pause between elements.
|
||||
|
||||
The 'power' command is used when powering on/off the DAC after the idle period (see -C option of squeezelite) and the 'speaker/headset' commands are sent when switching between speakers and headsets (see headset jack detection).
|
||||
|
||||
NB: For named configurations ((SqueezeAMP, Muse ... all except I2S), all this is ignored. For know codecs, the built-in sequences can be overwritten using dac_controlset
|
||||
|
||||
@@ -275,7 +283,7 @@ GPIO can be set to GND provide or Vcc at boot. This is convenient to power devic
|
||||
|
||||
The `<amp>` parameter can use used to assign a GPIO that will be set to active level (default 1) when playback starts. It will be reset when squeezelite becomes idle. The idle timeout is set on the squeezelite command line through `-C <timeout>`
|
||||
|
||||
The `<power>` parameter can use used to assign a GPIO that will be set to active level (default 1) when player is powered on and reset when powered off
|
||||
The `<power>` parameter can use used to assign a GPIO that will be set to active level (default 1) when player is powered on and reset when powered off (in LMS, does not apply to AirPlay, Spotify or BT).
|
||||
|
||||
If you have an audio jack that supports insertion (use :0 or :1 to set the level when inserted), you can specify which GPIO it's connected to. Using the parameter jack_mutes_amp allows to mute the amp when headset (e.g.) is inserted.
|
||||
|
||||
@@ -319,18 +327,18 @@ See [set_GPIO](#set-gpio) for how to set the green and red LEDs (including addre
|
||||
NB: For named configuration, GPIO affected to green and red LED cannot be changed but brightness option applies
|
||||
|
||||
### LED Strip
|
||||
One LED strip with up to 255 addressable LEDs can be configured to offer enhanced visualizations. The LED strip can also be controlled remotely though the LMS server (using the CLI interface). Currently only WS2812B LEDs are supported. Set the LED Strip configuration (or NVS led_vu_config) to `WS2812,length=<n>,gpio=<gpio>, where <n> is the number of leds in the strip (1..255), and <gpio> is the data pin.`
|
||||
|
||||
The latest LMS plugin update is required to set the visualizer mode and brightness, in the ESP32 settings page for the player. The plugin also adds the following CLI command options
|
||||
One LED strip with up to 255 addressable LEDs can be configured to offer enhanced visualizations. The VU Meter visualizer includes a battery status indicator (see Battery). Currently only WS2812B LEDs are supported. Set the LED Strip hardware configuration, or the NVS led_vu_config syntax is
|
||||
```
|
||||
<playerid> led_visual [<mode>] [brightness(1-255)]
|
||||
Toggles or selects the visulaizer mode.
|
||||
The visualizer brighness can be controled using the optional <brighness> tag.
|
||||
|
||||
<playerid> dmx <R,G,B|R,G,B,R,G,B ... R,G,B> [<offset>]
|
||||
Sets the LED at position "offset" to any RGB color where "R"(red),"G"(green), and "B"(blue) are values from 0(off) to 255(max brightness).
|
||||
Add additional RGB values to the delimited string to set multiple LEDs.
|
||||
type=[WS2812],length=<n>,gpio=<dataPin>[,scale=<gain>]
|
||||
```
|
||||
where `<n>` is the number of LEDs in the strip (1..255). A `<scale>` gain value (percentage) can be added to enhance effect responses.
|
||||
|
||||
The latest LMS plugin update is required to set the visualizer mode and brightness in the ESP32 Settings page for the player, or a controllable display (see Extra/SqueezeESP32 menus). The plugin adds additional LMS CLI commands.
|
||||
|
||||
| Command | Notes |
|
||||
| -------------------------------------------------- | ----------- |
|
||||
| \<playerid\> led_visual \[\<mode\>\] \[\<brightness\>\] | Toggles or selects the visualizer "mode".<br />The visualizer brightness(0..255) can be controlled using the "brightness" tag. |
|
||||
| \<playerid\> dmx \<R,G,B,R,G,B, ... R,G,B\> \[\<offset\>\] | Sets the LED color starting at position "offset"<br /> with "R"(red),"G"(green),and "B"(blue) color sequences.<br />Add additional RGB values to the delimited string to set multiple LEDs.<br /> |
|
||||
|
||||
### Rotary Encoder
|
||||
One rotary encoder is supported, quadrature shift with press. Such encoders usually have 2 pins for encoders (A and B), and common C that must be set to ground and an optional SW pin for press. A, B and SW must be pulled up, so automatic pull-up is provided by ESP32, but you can add your own resistors. A bit of filtering on A and B (~470nF) helps for debouncing which is not made by software.
|
||||
|
||||
176
components/display/SH1122.c
Normal file
176
components/display/SH1122.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* 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[] = "SH1122";
|
||||
|
||||
struct PrivateSpace {
|
||||
uint8_t *iRAM, *Shadowbuffer;
|
||||
uint8_t PageSize;
|
||||
};
|
||||
|
||||
// Functions are not declared to minimize # of lines
|
||||
|
||||
static void SetColumnAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
||||
Device->WriteCommand( Device, 0x10 | (Start >> 4) );
|
||||
Device->WriteCommand( Device, 0x00 | (Start & 0x0f) );
|
||||
}
|
||||
|
||||
static void SetRowAddress( struct GDS_Device* Device, uint8_t Start, uint8_t End ) {
|
||||
Device->WriteCommand( Device, 0xB0 );
|
||||
Device->WriteCommand( Device, Start );
|
||||
}
|
||||
|
||||
static void Update( struct GDS_Device* Device ) {
|
||||
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||
|
||||
// RAM is by columns of 4 pixels ...
|
||||
SetColumnAddress( Device, 0, 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) {
|
||||
SetRowAddress( Device, r - page + 1, r );
|
||||
if (Private->iRAM) {
|
||||
memcpy(Private->iRAM, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2 );
|
||||
Device->WriteData( Device, Private->iRAM, Device->Width * page / 2 );
|
||||
} else {
|
||||
Device->WriteData( Device, Private->Shadowbuffer + (r - page + 1) * Device->Width / 2, page * Device->Width / 2);
|
||||
}
|
||||
dirty = false;
|
||||
}
|
||||
page = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
SetRowAddress( Device, 0, Device->Height - 1 );
|
||||
for (int r = 0; r < Device->Height; r += Private->PageSize) {
|
||||
if (Private->iRAM) {
|
||||
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 SetLayout( struct GDS_Device* Device, struct GDS_Layout *Layout ) {
|
||||
if (Layout->HFlip) {
|
||||
Device->WriteCommand( Device, 0x40 + 0x20 );
|
||||
Device->WriteCommand( Device, 0xA1 );
|
||||
} else {
|
||||
Device->WriteCommand( Device, 0x40 + 0x00 );
|
||||
Device->WriteCommand( Device, 0xA0 );
|
||||
}
|
||||
Device->WriteCommand( Device, Layout->VFlip ? 0xC8 : 0xC0 );
|
||||
Device->WriteCommand( Device, Layout->Invert ? 0xA7 : 0xA6 );
|
||||
}
|
||||
|
||||
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, 0x81 );
|
||||
Device->WriteCommand( Device, Contrast );
|
||||
}
|
||||
|
||||
static bool Init( struct GDS_Device* Device ) {
|
||||
struct PrivateSpace *Private = (struct PrivateSpace*) Device->Private;
|
||||
|
||||
// find a page size that is not too small is an integer of height
|
||||
Private->PageSize = min(8, PAGE_BLOCK / (Device->Width / 2));
|
||||
while (Private->PageSize && Device->Height != (Device->Height / Private->PageSize) * Private->PageSize) Private->PageSize--;
|
||||
|
||||
#ifdef SHADOW_BUFFER
|
||||
Private->Shadowbuffer = malloc( Device->FramebufferSize );
|
||||
memset(Private->Shadowbuffer, 0xFF, Device->FramebufferSize);
|
||||
#endif
|
||||
|
||||
// only use iRAM for SPI
|
||||
if (Device->IF == GDS_IF_SPI) {
|
||||
Private->iRAM = heap_caps_malloc( Private->PageSize * Device->Width / 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA );
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "SH1122 page %u, iRAM %p", Private->PageSize, Private->iRAM);
|
||||
|
||||
// need to be off and disable display RAM
|
||||
Device->DisplayOff( Device );
|
||||
Device->WriteCommand( Device, 0xA5 );
|
||||
|
||||
// Display Offset
|
||||
Device->WriteCommand( Device, 0xD3 );
|
||||
Device->WriteCommand( Device, 0 );
|
||||
|
||||
// set flip modes
|
||||
struct GDS_Layout Layout = { };
|
||||
Device->SetLayout( Device, &Layout );
|
||||
|
||||
// set Clocks => check value
|
||||
Device->WriteCommand( Device, 0xD5 );
|
||||
Device->WriteCommand( Device, ( 0x04 << 4 ) | 0x00 );
|
||||
|
||||
// MUX Ratio => fixed
|
||||
Device->WriteCommand( Device, 0xA8 );
|
||||
Device->WriteCommand( Device, Device->Height - 1);
|
||||
|
||||
// no Display Inversion
|
||||
Device->WriteCommand( Device, 0xA6 );
|
||||
|
||||
// gone with the wind
|
||||
Device->WriteCommand( Device, 0xA4 );
|
||||
Device->DisplayOn( Device );
|
||||
Device->Update( Device );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct GDS_Device SH1122 = {
|
||||
.DisplayOn = DisplayOn, .DisplayOff = DisplayOff, .SetContrast = SetContrast,
|
||||
.SetLayout = SetLayout,
|
||||
.Update = Update, .Init = Init,
|
||||
.Mode = GDS_GRAYSCALE, .Depth = 4,
|
||||
.HighNibble = true,
|
||||
};
|
||||
|
||||
struct GDS_Device* SH1122_Detect(char *Driver, struct GDS_Device* Device) {
|
||||
if (!strcasestr(Driver, "SH1122")) return NULL;
|
||||
|
||||
if (!Device) Device = calloc(1, sizeof(struct GDS_Device));
|
||||
*Device = SH1122;
|
||||
|
||||
return Device;
|
||||
}
|
||||
@@ -71,8 +71,8 @@ static void Update( struct GDS_Device* Device ) {
|
||||
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 );
|
||||
// need byte swapping
|
||||
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;
|
||||
@@ -84,14 +84,10 @@ static void Update( struct GDS_Device* Device ) {
|
||||
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 );
|
||||
}
|
||||
// need byte swapping
|
||||
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);
|
||||
Device->WriteData( Device, Private->iRAM, Private->PageSize * Device->Width / 2 );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
|
||||
int c = x1;
|
||||
// for a row that is not on a boundary, no optimization possible
|
||||
while (r & 0x07 && r <= y2) {
|
||||
for (c = x1; c <= x2; c++) DrawPixelFast( Device, c, r, Color );
|
||||
for (c = x1; c <= x2; c++) Device->DrawPixelFast( Device, c, r, Color );
|
||||
r++;
|
||||
}
|
||||
// go fast if we have more than 8 lines to write
|
||||
@@ -117,7 +117,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
|
||||
memset(optr + Width * r + x1, _Color, x2 - x1 + 1);
|
||||
r += 8;
|
||||
} else while (r <= y2) {
|
||||
for (c = x1; c <= x2; c++) DrawPixelFast( Device, c, r, Color );
|
||||
for (c = x1; c <= x2; c++) Device->DrawPixelFast( Device, c, r, Color );
|
||||
r++;
|
||||
}
|
||||
}
|
||||
@@ -133,10 +133,10 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
|
||||
// try to do byte processing as much as possible
|
||||
for (int r = y1; r <= y2; r++) {
|
||||
int c = x1;
|
||||
if (c & 0x01) DrawPixelFast( Device, c++, r, Color);
|
||||
if (c & 0x01) Device->DrawPixelFast( Device, c++, r, Color);
|
||||
int chunk = (x2 - c + 1) >> 1;
|
||||
memset(optr + ((r * Width + c) >> 1), _Color, chunk);
|
||||
if (c + chunk <= x2) DrawPixelFast( Device, x2, r, Color);
|
||||
if (c + chunk <= x2) Device->DrawPixelFast( Device, x2, r, Color);
|
||||
}
|
||||
}
|
||||
} else if (Device->Depth == 8) {
|
||||
@@ -148,7 +148,7 @@ void GDS_ClearWindow( struct GDS_Device* Device, int x1, int y1, int x2, int y2,
|
||||
} else {
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
DrawPixelFast( Device, x, y, Color);
|
||||
Device->DrawPixelFast( Device, x, y, Color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,10 +171,76 @@ bool GDS_Reset( struct GDS_Device* Device ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void IRAM_ATTR DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint32_t YBit = ( Y & 0x07 );
|
||||
uint8_t* FBOffset;
|
||||
|
||||
/*
|
||||
* We only need to modify the Y coordinate since the pitch
|
||||
* of the screen is the same as the width.
|
||||
* Dividing Y by 8 gives us which row the pixel is in but not
|
||||
* the bit position.
|
||||
*/
|
||||
Y>>= 3;
|
||||
|
||||
FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
|
||||
|
||||
if ( Color == GDS_COLOR_XOR ) {
|
||||
*FBOffset ^= BIT( YBit );
|
||||
} else {
|
||||
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit );
|
||||
}
|
||||
}
|
||||
|
||||
static void IRAM_ATTR DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint8_t* FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
|
||||
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f));
|
||||
}
|
||||
|
||||
static void IRAM_ATTR DrawPixel4FastHigh( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint8_t* FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
|
||||
*FBOffset = X & 0x01 ? ((*FBOffset & 0xf0) | (Color & 0x0f)) : (*FBOffset & 0x0f) | ((Color & 0x0f) << 4);
|
||||
}
|
||||
|
||||
static void IRAM_ATTR DrawPixel8Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
Device->Framebuffer[Y * Device->Width + X] = Color;
|
||||
}
|
||||
|
||||
// assumes that Color is 16 bits R..RG..GB..B from MSB to LSB and FB wants 1st serialized byte to start with R
|
||||
static void IRAM_ATTR DrawPixel16Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint16_t* FBOffset = (uint16_t*) Device->Framebuffer + Y * Device->Width + X;
|
||||
*FBOffset = __builtin_bswap16(Color);
|
||||
}
|
||||
|
||||
// assumes that Color is 18 bits RGB from MSB to LSB RRRRRRGGGGGGBBBBBB, so byte[0] is B
|
||||
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = xxRRRRRR xxGGGGGG xxBBBBBB
|
||||
static void IRAM_ATTR DrawPixel18Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
|
||||
*FBOffset++ = Color >> 12; *FBOffset++ = (Color >> 6) & 0x3f; *FBOffset = Color & 0x3f;
|
||||
}
|
||||
|
||||
// assumes that Color is 24 bits RGB from MSB to LSB RRRRRRRRGGGGGGGGBBBBBBBB, so byte[0] is B
|
||||
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = RRRRRRRR GGGGGGGG BBBBBBBB
|
||||
static void IRAM_ATTR DrawPixel24Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
|
||||
*FBOffset++ = Color >> 16; *FBOffset++ = Color >> 8; *FBOffset = Color;
|
||||
}
|
||||
|
||||
bool GDS_Init( struct GDS_Device* Device ) {
|
||||
|
||||
if (Device->Depth > 8) Device->FramebufferSize = Device->Width * Device->Height * ((8 + Device->Depth - 1) / 8);
|
||||
else Device->FramebufferSize = (Device->Width * Device->Height) / (8 / Device->Depth);
|
||||
|
||||
// set the proper DrawPixel function if not already set by driver
|
||||
if (!Device->DrawPixelFast) {
|
||||
if (Device->Depth == 1) Device->DrawPixelFast = DrawPixel1Fast;
|
||||
else if (Device->Depth == 4 && Device->HighNibble) Device->DrawPixelFast = DrawPixel4FastHigh;
|
||||
else if (Device->Depth == 4) Device->DrawPixelFast = DrawPixel4Fast;
|
||||
else if (Device->Depth == 8) Device->DrawPixelFast = DrawPixel8Fast;
|
||||
else if (Device->Depth == 16) Device->DrawPixelFast = DrawPixel16Fast;
|
||||
else if (Device->Depth == 24 && Device->Mode == GDS_RGB666) Device->DrawPixelFast = DrawPixel18Fast;
|
||||
else if (Device->Depth == 24 && Device->Mode == GDS_RGB888) Device->DrawPixelFast = DrawPixel24Fast;
|
||||
}
|
||||
|
||||
// allocate FB unless explicitely asked not to
|
||||
if (!(Device->Alloc & GDS_ALLOC_NONE)) {
|
||||
|
||||
@@ -45,7 +45,7 @@ __attribute__( ( always_inline ) ) static inline void SwapInt( int* a, int* b )
|
||||
}
|
||||
|
||||
void IRAM_ATTR GDS_DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
DrawPixelFast( Device, X, Y, Color );
|
||||
Device->DrawPixelFast( Device, X, Y, Color );
|
||||
}
|
||||
|
||||
void IRAM_ATTR GDS_DrawPixel( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
@@ -63,7 +63,7 @@ void GDS_DrawHLine( struct GDS_Device* Device, int x, int y, int Width, int Colo
|
||||
if (y < 0) y = 0;
|
||||
else if (y >= Device->Height) y = Device->Height - 1;
|
||||
|
||||
for ( ; x < XEnd; x++ ) DrawPixelFast( Device, x, y, Color );
|
||||
for ( ; x < XEnd; x++ ) Device->DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
|
||||
void GDS_DrawVLine( struct GDS_Device* Device, int x, int y, int Height, int Color ) {
|
||||
@@ -97,7 +97,7 @@ static inline void DrawWideLine( struct GDS_Device* Device, int x0, int y0, int
|
||||
|
||||
for ( ; x < x1; x++ ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
DrawPixelFast( Device, x, y, Color );
|
||||
Device->DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
|
||||
if ( Error > 0 ) {
|
||||
@@ -126,7 +126,7 @@ static inline void DrawTallLine( struct GDS_Device* Device, int x0, int y0, int
|
||||
|
||||
for ( ; y < y1; y++ ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
DrawPixelFast( Device, x, y, Color );
|
||||
Device->DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
|
||||
if ( Error > 0 ) {
|
||||
@@ -213,37 +213,65 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
|
||||
iptr += Height;
|
||||
}
|
||||
}
|
||||
} else if (Device->Depth == 4) {
|
||||
} else if (Device->Depth == 4) {
|
||||
uint8_t *optr = Device->Framebuffer;
|
||||
int LineLen = Device->Width >> 1;
|
||||
|
||||
Height >>= 3;
|
||||
Color &= 0x0f;
|
||||
|
||||
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
|
||||
uint8_t Byte = BitReverseTable256[*Data++];
|
||||
// we need to linearize code to let compiler better optimize
|
||||
if (c & 0x01) {
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen;
|
||||
} else {
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen;
|
||||
}
|
||||
// end of a column, move to next one
|
||||
if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); }
|
||||
|
||||
if (Device->HighNibble) {
|
||||
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
|
||||
uint8_t Byte = BitReverseTable256[*Data++];
|
||||
// we need to linearize code to let compiler better optimize
|
||||
if (c & 0x01) {
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen;
|
||||
} else {
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen;
|
||||
}
|
||||
// end of a column, move to next one
|
||||
if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); }
|
||||
}
|
||||
} else {
|
||||
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
|
||||
uint8_t Byte = BitReverseTable256[*Data++];
|
||||
// we need to linearize code to let compiler better optimize
|
||||
if (c & 0x01) {
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0x0f) | (((Byte & 0x01)*Color)<<4); optr += LineLen;
|
||||
} else {
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen; Byte >>= 1;
|
||||
*optr = (*optr & 0xf0) | (((Byte & 0x01)*Color)); optr += LineLen;
|
||||
}
|
||||
// end of a column, move to next one
|
||||
if (++r == Height) { c++; r = 0; optr = Device->Framebuffer + (c >> 1); }
|
||||
}
|
||||
}
|
||||
} else if (Device->Depth == 8) {
|
||||
uint8_t *optr = Device->Framebuffer;
|
||||
@@ -320,14 +348,14 @@ void GDS_DrawBitmapCBR(struct GDS_Device* Device, uint8_t *Data, int Width, int
|
||||
// don't know bitdepth, use brute-force solution
|
||||
for (int i = Width * Height, r = 0, c = 0; --i >= 0;) {
|
||||
uint8_t Byte = *Data++;
|
||||
DrawPixelFast( Device, c, (r << 3) + 7, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
DrawPixelFast( Device, c, (r << 3) + 6, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
DrawPixelFast( Device, c, (r << 3) + 5, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
DrawPixelFast( Device, c, (r << 3) + 4, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
DrawPixelFast( Device, c, (r << 3) + 3, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
DrawPixelFast( Device, c, (r << 3) + 2, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
DrawPixelFast( Device, c, (r << 3) + 1, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
DrawPixelFast( Device, c, (r << 3) + 0, (Byte & 0x01) * Color );
|
||||
Device->DrawPixelFast( Device, c, (r << 3) + 7, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
Device->DrawPixelFast( Device, c, (r << 3) + 6, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
Device->DrawPixelFast( Device, c, (r << 3) + 5, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
Device->DrawPixelFast( Device, c, (r << 3) + 4, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
Device->DrawPixelFast( Device, c, (r << 3) + 3, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
Device->DrawPixelFast( Device, c, (r << 3) + 2, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
Device->DrawPixelFast( Device, c, (r << 3) + 1, (Byte & 0x01) * Color ); Byte >>= 1;
|
||||
Device->DrawPixelFast( Device, c, (r << 3) + 0, (Byte & 0x01) * Color );
|
||||
if (++r == Height) { c++; r = 0; }
|
||||
}
|
||||
/* for better understanding, here is the mundane version
|
||||
|
||||
@@ -98,6 +98,7 @@ struct GDS_Device {
|
||||
uint16_t Width, TextWidth;
|
||||
uint16_t Height;
|
||||
uint8_t Depth, Mode;
|
||||
bool HighNibble;
|
||||
|
||||
uint8_t Alloc;
|
||||
uint8_t* Framebuffer;
|
||||
@@ -155,69 +156,9 @@ static inline bool IsPixelVisible( struct GDS_Device* Device, int x, int y ) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
static inline void DrawPixel1Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint32_t YBit = ( Y & 0x07 );
|
||||
uint8_t* FBOffset;
|
||||
|
||||
/*
|
||||
* We only need to modify the Y coordinate since the pitch
|
||||
* of the screen is the same as the width.
|
||||
* Dividing Y by 8 gives us which row the pixel is in but not
|
||||
* the bit position.
|
||||
*/
|
||||
Y>>= 3;
|
||||
|
||||
FBOffset = Device->Framebuffer + ( ( Y * Device->Width ) + X );
|
||||
|
||||
if ( Color == GDS_COLOR_XOR ) {
|
||||
*FBOffset ^= BIT( YBit );
|
||||
} else {
|
||||
*FBOffset = ( Color == GDS_COLOR_BLACK ) ? *FBOffset & ~BIT( YBit ) : *FBOffset | BIT( YBit );
|
||||
}
|
||||
}
|
||||
|
||||
static inline void DrawPixel4Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint8_t* FBOffset = Device->Framebuffer + ( (Y * Device->Width >> 1) + (X >> 1));
|
||||
*FBOffset = X & 0x01 ? (*FBOffset & 0x0f) | ((Color & 0x0f) << 4) : ((*FBOffset & 0xf0) | (Color & 0x0f));
|
||||
}
|
||||
|
||||
static inline void DrawPixel8Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
Device->Framebuffer[Y * Device->Width + X] = Color;
|
||||
}
|
||||
|
||||
// assumes that Color is 16 bits R..RG..GB..B from MSB to LSB and FB wants 1st serialized byte to start with R
|
||||
static inline void DrawPixel16Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint16_t* FBOffset = (uint16_t*) Device->Framebuffer + Y * Device->Width + X;
|
||||
*FBOffset = __builtin_bswap16(Color);
|
||||
}
|
||||
|
||||
// assumes that Color is 18 bits RGB from MSB to LSB RRRRRRGGGGGGBBBBBB, so byte[0] is B
|
||||
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = xxRRRRRR xxGGGGGG xxBBBBBB
|
||||
static inline void DrawPixel18Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
|
||||
*FBOffset++ = Color >> 12; *FBOffset++ = (Color >> 6) & 0x3f; *FBOffset = Color & 0x3f;
|
||||
}
|
||||
|
||||
// assumes that Color is 24 bits RGB from MSB to LSB RRRRRRRRGGGGGGGGBBBBBBBB, so byte[0] is B
|
||||
// FB is 3-bytes packets and starts with R for serialization so 0,1,2 ... = RRRRRRRR GGGGGGGG BBBBBBBB
|
||||
static inline void DrawPixel24Fast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
uint8_t* FBOffset = Device->Framebuffer + (Y * Device->Width + X) * 3;
|
||||
*FBOffset++ = Color >> 16; *FBOffset++ = Color >> 8; *FBOffset = Color;
|
||||
}
|
||||
|
||||
static inline void IRAM_ATTR DrawPixelFast( struct GDS_Device* Device, int X, int Y, int Color ) {
|
||||
if (Device->DrawPixelFast) Device->DrawPixelFast( Device, X, Y, Color );
|
||||
else if (Device->Depth == 4) DrawPixel4Fast( Device, X, Y, Color );
|
||||
else if (Device->Depth == 1) DrawPixel1Fast( Device, X, Y, Color );
|
||||
else if (Device->Depth == 16) DrawPixel16Fast( Device, X, Y, Color );
|
||||
else if (Device->Depth == 24 && Device->Mode == GDS_RGB666) DrawPixel18Fast( Device, X, Y, Color );
|
||||
else if (Device->Depth == 24 && Device->Mode == GDS_RGB888) DrawPixel24Fast( Device, X, Y, Color );
|
||||
else if (Device->Depth == 8) DrawPixel8Fast( Device, X, Y, Color );
|
||||
}
|
||||
|
||||
static inline void IRAM_ATTR DrawPixel( struct GDS_Device* Device, int x, int y, int Color ) {
|
||||
if ( IsPixelVisible( Device, x, y ) == true ) {
|
||||
DrawPixelFast( Device, x, y, Color );
|
||||
Device->DrawPixelFast( Device, x, y, Color );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ bool GDS_TextLine(struct GDS_Device* Device, int N, int Pos, int Attr, char *Tex
|
||||
int Y_min = max(0, Device->Lines[N].Y), Y_max = max(0, Device->Lines[N].Y + Device->Lines[N].Font->Height);
|
||||
for (int c = (Attr & GDS_TEXT_CLEAR_EOL) ? X : 0; c < Device->TextWidth; c++)
|
||||
for (int y = Y_min; y < Y_max; y++)
|
||||
DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
|
||||
Device->DrawPixelFast( Device, c, y, GDS_COLOR_BLACK );
|
||||
}
|
||||
|
||||
GDS_FontDrawString( Device, X, Device->Lines[N].Y, Text, GDS_COLOR_WHITE );
|
||||
|
||||
@@ -63,6 +63,7 @@ static EXT_RAM_ATTR struct {
|
||||
} displayer;
|
||||
|
||||
static const char *known_drivers[] = {"SH1106",
|
||||
"SH1122",
|
||||
"SSD1306",
|
||||
"SSD1322",
|
||||
"SSD1326",
|
||||
@@ -79,8 +80,8 @@ static void displayer_task(void *args);
|
||||
static void display_sleep(void);
|
||||
|
||||
struct GDS_Device *display;
|
||||
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect;
|
||||
GDS_DetectFunc *drivers[] = { SH1106_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, NULL };
|
||||
extern GDS_DetectFunc SSD1306_Detect, SSD132x_Detect, SH1106_Detect, SH1122_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect;
|
||||
GDS_DetectFunc *drivers[] = { SH1106_Detect, SH1122_Detect, SSD1306_Detect, SSD132x_Detect, SSD1675_Detect, SSD1322_Detect, SSD1351_Detect, ST77xx_Detect, ILI9341_Detect, NULL };
|
||||
|
||||
/****************************************************************************************
|
||||
*
|
||||
|
||||
@@ -293,11 +293,8 @@ bool led_strip_init(struct led_strip_t *led_strip)
|
||||
static EXT_RAM_ATTR StackType_t xStack[LED_STRIP_TASK_SIZE] __attribute__ ((aligned (4)));
|
||||
|
||||
if ((led_strip == NULL) ||
|
||||
(led_strip->rmt_channel >= RMT_CHANNEL_MAX) ||
|
||||
(led_strip->gpio > GPIO_NUM_33) ||
|
||||
(led_strip->led_strip_working == NULL) ||
|
||||
(led_strip->led_strip_showing == NULL) ||
|
||||
(led_strip->led_strip_length == 0) ||
|
||||
(led_strip->access_semaphore == NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "monitor.h"
|
||||
#include "led_strip.h"
|
||||
#include "platform_config.h"
|
||||
#include "services.h"
|
||||
#include "led_vu.h"
|
||||
|
||||
static const char *TAG = "led_vu";
|
||||
@@ -55,6 +56,7 @@ static EXT_RAM_ATTR struct {
|
||||
int vu_start_l;
|
||||
int vu_start_r;
|
||||
int vu_status;
|
||||
int vu_scale;
|
||||
} strip;
|
||||
|
||||
static int led_addr(int pos ) {
|
||||
@@ -70,31 +72,31 @@ static void battery_svc(float value, int cells) {
|
||||
if (battery_handler_chain) battery_handler_chain(value, cells);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Suspend.
|
||||
*
|
||||
*/
|
||||
static void led_vu_sleep(void) {
|
||||
led_vu_clear(led_display);
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Initialize the led vu strip if configured.
|
||||
*
|
||||
*/
|
||||
void led_vu_init()
|
||||
{
|
||||
char* p;
|
||||
char* config = config_alloc_get_str("led_vu_config", NULL, "N/A");
|
||||
|
||||
// Initialize led VU strip
|
||||
char* drivername = strcasestr(config, "WS2812");
|
||||
|
||||
if ((p = strcasestr(config, "length")) != NULL) {
|
||||
strip.length = atoi(strchr(p, '=') + 1);
|
||||
} // else 0
|
||||
if ((p = strcasestr(config, "gpio")) != NULL) {
|
||||
strip.gpio = atoi(strchr(p, '=') + 1);
|
||||
} else {
|
||||
strip.gpio = LED_VU_DEFAULT_GPIO;
|
||||
}
|
||||
PARSE_PARAM(config, "length",'=', strip.length);
|
||||
PARSE_PARAM(config, "gpio",'=', strip.gpio);
|
||||
// check for valid configuration
|
||||
if (!drivername || !strip.gpio) {
|
||||
if (!strip.gpio) {
|
||||
ESP_LOGI(TAG, "led_vu configuration invalid");
|
||||
goto done;
|
||||
}
|
||||
strip.vu_scale = 100;
|
||||
PARSE_PARAM(config, "scale",'=',strip.vu_scale);
|
||||
|
||||
battery_handler_chain = battery_handler_svc;
|
||||
battery_handler_svc = battery_svc;
|
||||
@@ -114,7 +116,7 @@ void led_vu_init()
|
||||
strip.vu_start_r = strip.vu_length + 1;
|
||||
strip.vu_status = strip.vu_length;
|
||||
}
|
||||
ESP_LOGI(TAG, "vu meter using length:%d left:%d right:%d status:%d", strip.vu_length, strip.vu_start_l, strip.vu_start_r, strip.vu_status);
|
||||
ESP_LOGI(TAG, "vu meter using length:%d left:%d right:%d status:%d scale:%d", strip.vu_length, strip.vu_start_l, strip.vu_start_r, strip.vu_status, strip.vu_scale);
|
||||
|
||||
// create driver configuration
|
||||
led_strip_config.rgb_led_type = RGB_LED_TYPE_WS2812;
|
||||
@@ -138,6 +140,8 @@ void led_vu_init()
|
||||
// reserver max memory for remote management systems
|
||||
rmt_set_mem_block_num(led_strip_config.rmt_channel, 7);
|
||||
|
||||
services_sleep_setsuspend(led_vu_sleep);
|
||||
|
||||
led_vu_clear(led_display);
|
||||
|
||||
done:
|
||||
@@ -157,6 +161,14 @@ uint16_t led_vu_string_length() {
|
||||
return (uint16_t)strip.length;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Returns a user defined scale (percent)
|
||||
*/
|
||||
uint16_t led_vu_scale() {
|
||||
if (!led_display) return 0;
|
||||
return (uint16_t)strip.vu_scale;
|
||||
}
|
||||
|
||||
/****************************************************************************************
|
||||
* Turns all LEDs off (Black)
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
extern struct led_strip_t* led_display;
|
||||
|
||||
uint16_t led_vu_string_length();
|
||||
uint16_t led_vu_scale();
|
||||
void led_vu_progress_bar(int pct, int bright);
|
||||
void led_vu_display(int vu_l, int vu_r, int bright, bool comet);
|
||||
void led_vu_spin_dial(int gain, int rate, int speed, bool comet);
|
||||
|
||||
@@ -538,7 +538,7 @@ bool config_set_group_bit(int bit_num,bool flag){
|
||||
return result;
|
||||
}
|
||||
|
||||
void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size) {
|
||||
void config_set_default(nvs_type_t type, const char *key, const void * default_value, size_t blob_size) {
|
||||
if(!config_lock(LOCK_MAX_WAIT/portTICK_PERIOD_MS)){
|
||||
ESP_LOGE(TAG, "Unable to lock config");
|
||||
return;
|
||||
|
||||
@@ -56,7 +56,7 @@ cJSON * config_alloc_get_cjson(const char *key);
|
||||
esp_err_t config_set_cjson_str_and_free(const char *key, cJSON *value);
|
||||
void config_get_uint16t_from_str(const char *key, uint16_t *value, uint16_t default_value);
|
||||
void config_delete_key(const char *key);
|
||||
void config_set_default(nvs_type_t type, const char *key, void * default_value, size_t blob_size);
|
||||
void config_set_default(nvs_type_t type, const char *key, const void * default_value, size_t blob_size);
|
||||
void * config_alloc_get(nvs_type_t nvs_type, const char *key) ;
|
||||
bool wait_for_commit();
|
||||
char * config_alloc_get_json(bool bFormatted);
|
||||
|
||||
@@ -43,11 +43,15 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
|
||||
extern void register_audio_config(void);
|
||||
extern void register_rotary_config(void);
|
||||
extern void register_ledvu_config(void);
|
||||
extern void register_nvs();
|
||||
|
||||
void register_optional_cmd(void) {
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
register_rotary_config();
|
||||
register_ledvu_config();
|
||||
#endif
|
||||
register_audio_config();
|
||||
register_ledvu_config();
|
||||
register_nvs();
|
||||
}
|
||||
|
||||
extern int squeezelite_main(int argc, char **argv);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -64,8 +64,10 @@ static void register_heap();
|
||||
static void register_dump_heap();
|
||||
static void register_version();
|
||||
static void register_restart();
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
static void register_deep_sleep();
|
||||
static void register_light_sleep();
|
||||
#endif
|
||||
static void register_factory_boot();
|
||||
static void register_restart_ota();
|
||||
static void register_set_services();
|
||||
@@ -73,24 +75,42 @@ static void register_set_services();
|
||||
static void register_tasks();
|
||||
#endif
|
||||
extern BaseType_t network_manager_task;
|
||||
FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size){
|
||||
FILE *f = open_memstream(buf, buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(cmdname,MESSAGING_ERROR,"Unable to open memory stream.");
|
||||
}
|
||||
return f;
|
||||
}
|
||||
void register_system()
|
||||
{
|
||||
register_free();
|
||||
|
||||
register_setdevicename();
|
||||
register_set_services();
|
||||
register_free();
|
||||
register_heap();
|
||||
register_dump_heap();
|
||||
register_setdevicename();
|
||||
register_version();
|
||||
register_restart();
|
||||
register_deep_sleep();
|
||||
register_light_sleep();
|
||||
register_factory_boot();
|
||||
register_restart_ota();
|
||||
#if WITH_TASKS_INFO
|
||||
register_tasks();
|
||||
#endif
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
register_deep_sleep();
|
||||
register_light_sleep();
|
||||
#endif
|
||||
}
|
||||
void simple_restart()
|
||||
{
|
||||
log_send_messaging(MESSAGING_WARNING,"Rebooting.");
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
/* 'version' command */
|
||||
static int get_version(int argc, char **argv)
|
||||
{
|
||||
@@ -128,36 +148,23 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
|
||||
{
|
||||
if(is_recovery_running){
|
||||
if(partition_subtype ==ESP_PARTITION_SUBTYPE_APP_FACTORY){
|
||||
log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
return ESP_OK;
|
||||
// log_send_messaging(MESSAGING_WARNING,"RECOVERY application is already active");
|
||||
simple_restart();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(partition_subtype !=ESP_PARTITION_SUBTYPE_APP_FACTORY){
|
||||
log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
return ESP_OK;
|
||||
// log_send_messaging(MESSAGING_WARNING,"SQUEEZELITE application is already active");
|
||||
simple_restart();
|
||||
}
|
||||
}
|
||||
esp_err_t err = ESP_OK;
|
||||
bool bFound=false;
|
||||
log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
|
||||
// log_send_messaging(MESSAGING_INFO, "Looking for partition type %u",partition_subtype);
|
||||
const esp_partition_t *partition;
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, partition_subtype, NULL);
|
||||
|
||||
if(it == NULL){
|
||||
log_send_messaging(MESSAGING_ERROR,"Reboot failed. Cannot iterate through partitions");
|
||||
log_send_messaging(MESSAGING_ERROR,"Reboot failed. Partitions error");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -166,15 +173,11 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
|
||||
ESP_LOGD(TAG, "Releasing partition iterator");
|
||||
esp_partition_iterator_release(it);
|
||||
if(partition != NULL){
|
||||
log_send_messaging(MESSAGING_INFO, "Found application partition %s sub type %u", partition->label,partition_subtype);
|
||||
log_send_messaging(MESSAGING_INFO, "Rebooting to %s", partition->label);
|
||||
err=esp_ota_set_boot_partition(partition);
|
||||
if(err!=ESP_OK){
|
||||
bFound=false;
|
||||
log_send_messaging(MESSAGING_ERROR,"Unable to select partition for reboot: %s",esp_err_to_name(err));
|
||||
}
|
||||
else{
|
||||
bFound=true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -183,13 +186,7 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
|
||||
}
|
||||
ESP_LOGD(TAG, "Yielding to other processes");
|
||||
taskYIELD();
|
||||
if(bFound) {
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration changes. ");
|
||||
}
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
simple_restart();
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
@@ -197,46 +194,31 @@ esp_err_t guided_boot(esp_partition_subtype_t partition_subtype)
|
||||
|
||||
static int restart(int argc, char **argv)
|
||||
{
|
||||
log_send_messaging(MESSAGING_WARNING, "\n\nPerforming a simple restart to the currently active partition.");
|
||||
if(!wait_for_commit()){
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
simple_restart();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void simple_restart()
|
||||
{
|
||||
log_send_messaging(MESSAGING_WARNING,"System reboot requested.");
|
||||
if(!wait_for_commit()){
|
||||
log_send_messaging(MESSAGING_WARNING,"Unable to commit configuration. ");
|
||||
}
|
||||
|
||||
|
||||
vTaskDelay(750/ portTICK_PERIOD_MS);
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
esp_err_t guided_restart_ota(){
|
||||
log_send_messaging(MESSAGING_WARNING,"System reboot to Application requested");
|
||||
log_send_messaging(MESSAGING_WARNING,"Booting to Squeezelite");
|
||||
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
|
||||
return ESP_FAIL; // return fail. This should never return... we're rebooting!
|
||||
}
|
||||
esp_err_t guided_factory(){
|
||||
log_send_messaging(MESSAGING_WARNING,"System reboot to recovery requested");
|
||||
log_send_messaging(MESSAGING_WARNING,"Booting to recovery");
|
||||
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
|
||||
return ESP_FAIL; // return fail. This should never return... we're rebooting!
|
||||
}
|
||||
static int restart_factory(int argc, char **argv)
|
||||
{
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into recovery");
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Booting to Recovery");
|
||||
guided_boot(ESP_PARTITION_SUBTYPE_APP_FACTORY);
|
||||
return 0; // return fail. This should never return... we're rebooting!
|
||||
}
|
||||
static int restart_ota(int argc, char **argv)
|
||||
{
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Executing guided boot into ota app 0");
|
||||
cmd_send_messaging(argv[0],MESSAGING_WARNING, "Booting to Squeezelite");
|
||||
guided_boot(ESP_PARTITION_SUBTYPE_APP_OTA_0);
|
||||
return 0; // return fail. This should never return... we're rebooting!
|
||||
}
|
||||
@@ -248,7 +230,9 @@ static void register_restart()
|
||||
.hint = NULL,
|
||||
.func = &restart,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&cmd);
|
||||
#endif
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
static void register_restart_ota()
|
||||
@@ -259,7 +243,9 @@ static void register_restart_ota()
|
||||
.hint = NULL,
|
||||
.func = &restart_ota,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&cmd);
|
||||
#endif
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
|
||||
@@ -271,7 +257,9 @@ static void register_factory_boot()
|
||||
.hint = NULL,
|
||||
.func = &restart_factory,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&cmd);
|
||||
#endif
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
/** 'free' command prints available heap memory */
|
||||
@@ -287,11 +275,14 @@ static void register_free()
|
||||
{
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "free",
|
||||
.help = "Get the current size of free heap memory",
|
||||
.help = "Get free heap memory",
|
||||
.hint = NULL,
|
||||
.func = &free_mem,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&cmd);
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
static int dump_heap(int argc, char **argv)
|
||||
@@ -303,16 +294,16 @@ static int dump_heap(int argc, char **argv)
|
||||
/* 'heap' command prints minumum heap size */
|
||||
static int heap_size(int argc, char **argv)
|
||||
{
|
||||
ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
|
||||
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
|
||||
heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM),
|
||||
heap_caps_get_free_size(MALLOC_CAP_DMA),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_DMA),
|
||||
heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
|
||||
// ESP_LOGI(TAG,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
|
||||
// heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
|
||||
// heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
|
||||
// heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL),
|
||||
// heap_caps_get_free_size(MALLOC_CAP_SPIRAM),
|
||||
// heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM),
|
||||
// heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM),
|
||||
// heap_caps_get_free_size(MALLOC_CAP_DMA),
|
||||
// heap_caps_get_minimum_free_size(MALLOC_CAP_DMA),
|
||||
// heap_caps_get_largest_free_block(MALLOC_CAP_DMA));
|
||||
cmd_send_messaging(argv[0],MESSAGING_INFO,"Heap internal:%zu (min:%zu) (largest block:%zu)\nexternal:%zu (min:%zu) (largest block:%zd)\ndma :%zu (min:%zu) (largest block:%zd)",
|
||||
heap_caps_get_free_size(MALLOC_CAP_INTERNAL),
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL),
|
||||
@@ -457,9 +448,8 @@ static int setdevicename(int argc, char **argv)
|
||||
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
|
||||
return 1;
|
||||
}
|
||||
nerrors+=setnamevar("a2dp_dev_name", f, name);
|
||||
@@ -488,11 +478,13 @@ static void register_heap()
|
||||
{
|
||||
const esp_console_cmd_t heap_cmd = {
|
||||
.command = "heap",
|
||||
.help = "Get minimum size of free heap memory found during execution",
|
||||
.help = "Get minimum size of free heap memory",
|
||||
.hint = NULL,
|
||||
.func = &heap_size,
|
||||
};
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
cmd_to_json(&heap_cmd);
|
||||
#endif
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&heap_cmd) );
|
||||
|
||||
}
|
||||
@@ -521,6 +513,7 @@ static void register_setdevicename()
|
||||
.func = &setdevicename,
|
||||
.argtable = &name_args
|
||||
};
|
||||
|
||||
cmd_to_json_with_cb(&set_name,&setdevicename_cb);
|
||||
ESP_ERROR_CHECK(esp_console_cmd_register(&set_name));
|
||||
}
|
||||
@@ -561,7 +554,7 @@ static void register_tasks()
|
||||
|
||||
|
||||
/** 'deep_sleep' command puts the chip into deep sleep mode */
|
||||
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
static struct {
|
||||
struct arg_int *wakeup_time;
|
||||
struct arg_int *wakeup_gpio_num;
|
||||
@@ -618,15 +611,15 @@ static void register_deep_sleep()
|
||||
|
||||
const esp_console_cmd_t cmd = {
|
||||
.command = "deep_sleep",
|
||||
.help = "Enter deep sleep mode. "
|
||||
"Two wakeup modes are supported: timer and GPIO. "
|
||||
"If no wakeup option is specified, will sleep indefinitely.",
|
||||
.help = "Enter deep sleep mode. ",
|
||||
.hint = NULL,
|
||||
.func = &deep_sleep,
|
||||
.argtable = &deep_sleep_args
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
#endif
|
||||
|
||||
static int enable_disable(FILE * f,char * nvs_name, struct arg_lit *arg){
|
||||
esp_err_t err = config_set_value(NVS_TYPE_STR, nvs_name, arg->count>0?"Y":"N");
|
||||
const char * name = arg->hdr.longopts?arg->hdr.longopts:arg->hdr.glossary;
|
||||
@@ -649,9 +642,8 @@ static int do_set_services(int argc, char **argv)
|
||||
}
|
||||
char *buf = NULL;
|
||||
size_t buf_size = 0;
|
||||
FILE *f = open_memstream(&buf, &buf_size);
|
||||
FILE *f = system_open_memstream(argv[0],&buf, &buf_size);
|
||||
if (f == NULL) {
|
||||
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -674,7 +666,7 @@ static int do_set_services(int argc, char **argv)
|
||||
|
||||
if(err!=ESP_OK){
|
||||
nerrors++;
|
||||
fprintf(f,"Error setting telnet service to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
|
||||
fprintf(f,"Error setting telnet to %s. %s\n",set_services_args.telnet->sval[0], esp_err_to_name(err));
|
||||
}
|
||||
else {
|
||||
fprintf(f,"Telnet service changed to %s\n",set_services_args.telnet->sval[0]);
|
||||
@@ -706,7 +698,6 @@ cJSON * set_services_cb(){
|
||||
#if WITH_TASKS_INFO
|
||||
console_set_bool_parameter(values,"stats",set_services_args.stats);
|
||||
#endif
|
||||
|
||||
if ((p = config_alloc_get(NVS_TYPE_STR, "telnet_enable")) != NULL) {
|
||||
if(strcasestr("YX",p)!=NULL){
|
||||
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Telnet Only");
|
||||
@@ -717,7 +708,6 @@ cJSON * set_services_cb(){
|
||||
else {
|
||||
cJSON_AddStringToObject(values,set_services_args.telnet->hdr.longopts,"Disabled");
|
||||
}
|
||||
|
||||
FREE_AND_NULL(p);
|
||||
}
|
||||
|
||||
@@ -745,6 +735,8 @@ static void register_set_services(){
|
||||
cmd_to_json_with_cb(&cmd,&set_services_cb);
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
|
||||
#if CONFIG_WITH_CONFIG_UI
|
||||
static struct {
|
||||
struct arg_int *wakeup_time;
|
||||
struct arg_int *wakeup_gpio_num;
|
||||
@@ -836,4 +828,4 @@ static void register_light_sleep()
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,7 @@ void register_system();
|
||||
esp_err_t guided_factory();
|
||||
esp_err_t guided_restart_ota();
|
||||
void simple_restart();
|
||||
FILE * system_open_memstream(const char * cmdname,char **buf,size_t *buf_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -204,8 +204,10 @@ void register_wifi_join()
|
||||
|
||||
void register_wifi()
|
||||
{
|
||||
#ifdef WIFI_CMDLINE
|
||||
register_wifi_join();
|
||||
if(bypass_network_manager){
|
||||
initialise_wifi();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -461,7 +461,7 @@ static void buffer_put_packet(rtp_t *ctx, seq_t seqno, unsigned rtptime, bool fi
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (seqno == (u16_t) (ctx->ab_write+1)) {
|
||||
// expected packet
|
||||
abuf = ctx->audio_buffer + BUFIDX(seqno);
|
||||
@@ -572,17 +572,25 @@ static void buffer_push_packet(rtp_t *ctx) {
|
||||
}
|
||||
|
||||
LOG_SDEBUG("playtime %u %d [W:%hu R:%hu] %d", playtime, playtime - now, ctx->ab_write, ctx->ab_read, curframe->ready);
|
||||
|
||||
// try to request resend missing packet in order, explore up to 32 frames
|
||||
for (int step = max((ctx->ab_write - ctx->ab_read + 1) / 32, 1),
|
||||
i = 0, first = 0;
|
||||
seq_order(ctx->ab_read + i, ctx->ab_write); i += step) {
|
||||
|
||||
abuf_t* frame = ctx->audio_buffer + BUFIDX(ctx->ab_read + i);
|
||||
|
||||
// each missing packet will be requested up to (latency_frames / 16) times
|
||||
for (int i = 0; seq_order(ctx->ab_read + i, ctx->ab_write); i += 16) {
|
||||
abuf_t *frame = ctx->audio_buffer + BUFIDX(ctx->ab_read + i);
|
||||
if (!frame->ready && now - frame->last_resend > RESEND_TO) {
|
||||
// stop if one fails
|
||||
if (!rtp_request_resend(ctx, ctx->ab_read + i, ctx->ab_read + i)) break;
|
||||
frame->last_resend = now;
|
||||
}
|
||||
}
|
||||
|
||||
// stop when we reach a ready frame or a recent pending resend
|
||||
if (first && (frame->ready || now - frame->last_resend <= RESEND_TO)) {
|
||||
if (!rtp_request_resend(ctx, first, ctx->ab_read + i - 1)) break;
|
||||
first = 0;
|
||||
i += step - 1;
|
||||
} else if (!frame->ready && now - frame->last_resend > RESEND_TO) {
|
||||
if (!first) first = ctx->ab_read + i;
|
||||
frame->last_resend = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
@@ -329,7 +329,7 @@ esp_err_t config_ledvu_set(ledvu_struct_t * config){
|
||||
esp_err_t err=ESP_OK;
|
||||
char * config_buffer=malloc_init_external(buffer_size);
|
||||
if(config_buffer) {
|
||||
snprintf(config_buffer,buffer_size,"%s,length=%i,gpio=%i",config->type, config->length, config->gpio);
|
||||
snprintf(config_buffer,buffer_size,"%s,length=%i,gpio=%i,scale=%i",config->type, config->length, config->gpio, config->scale);
|
||||
log_send_messaging(MESSAGING_INFO,"Updating ledvu configuration to %s",config_buffer);
|
||||
err = config_set_value(NVS_TYPE_STR, "led_vu_config", config_buffer);
|
||||
if(err!=ESP_OK){
|
||||
@@ -761,14 +761,13 @@ const rotary_struct_t * config_rotary_get() {
|
||||
*/
|
||||
const ledvu_struct_t * config_ledvu_get() {
|
||||
|
||||
static ledvu_struct_t ledvu={ .type = "WS2812", .gpio = -1, .length = 0};
|
||||
static ledvu_struct_t ledvu={ .type = "WS2812", .gpio = -1, .length = 0, .scale= 100 };
|
||||
char *config = config_alloc_get_default(NVS_TYPE_STR, "led_vu_config", NULL, 0);
|
||||
if (config && *config) {
|
||||
char *p;
|
||||
|
||||
// ToDo: Add code for future support of alternate led types
|
||||
if ((p = strcasestr(config, "gpio")) != NULL) ledvu.gpio = atoi(strchr(p, '=') + 1);
|
||||
if ((p = strcasestr(config, "length")) != NULL) ledvu.length = atoi(strchr(p, '=') + 1);
|
||||
PARSE_PARAM_STR(config, "type", '=', ledvu.type, 15);
|
||||
PARSE_PARAM(config, "gpio", '=', ledvu.gpio);
|
||||
PARSE_PARAM(config, "length", '=', ledvu.length);
|
||||
PARSE_PARAM(config, "scale", '=', ledvu.scale);
|
||||
free(config);
|
||||
}
|
||||
return &ledvu;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "driver/i2s.h"
|
||||
#include "driver/spi_master.h"
|
||||
#include "gpio_exp.h"
|
||||
|
||||
#include "cJSON.h"
|
||||
extern const char *i2c_name_type;
|
||||
extern const char *spi_name_type;
|
||||
|
||||
@@ -89,6 +89,7 @@ typedef struct {
|
||||
char type[16];
|
||||
int length;
|
||||
int gpio;
|
||||
int scale;
|
||||
} ledvu_struct_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -62,7 +62,7 @@ static void task_stats( cJSON* top ) {
|
||||
current.n = uxTaskGetSystemState( current.tasks, current.n, ¤t.total );
|
||||
cJSON_AddNumberToObject(top,"ntasks",current.n);
|
||||
|
||||
char scratch[SCRATCH_SIZE] = { };
|
||||
char scratch[SCRATCH_SIZE] = {0};
|
||||
|
||||
#ifdef CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
|
||||
#pragma message("Compiled with runtime stats")
|
||||
@@ -143,11 +143,13 @@ static void monitor_trace(uint32_t now) {
|
||||
heap_caps_get_minimum_free_size(MALLOC_CAP_DMA));
|
||||
|
||||
task_stats(top);
|
||||
|
||||
char * top_a= cJSON_PrintUnformatted(top);
|
||||
if(top_a){
|
||||
messaging_post_message(MESSAGING_INFO, MESSAGING_CLASS_STATS,top_a);
|
||||
FREE_AND_NULL(top_a);
|
||||
}
|
||||
|
||||
cJSON_Delete(top);
|
||||
}
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ void services_init(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// set potential power GPIO on chip first in case expanders are power using these
|
||||
// set potential power GPIO on chip first in case expanders are powered using these
|
||||
parse_set_GPIO(set_chip_power_gpio);
|
||||
|
||||
// shared I2C bus
|
||||
|
||||
@@ -53,6 +53,7 @@ private:
|
||||
std::atomic<states> state;
|
||||
std::string credentials;
|
||||
bool zeroConf;
|
||||
std::atomic<bool> flushed = false, notify = true;
|
||||
|
||||
int startOffset, volume = 0, bitrate = 160;
|
||||
httpd_handle_t serverHandle;
|
||||
@@ -60,6 +61,7 @@ private:
|
||||
cspot_cmd_cb_t cmdHandler;
|
||||
cspot_data_cb_t dataHandler;
|
||||
std::string lastTrackId;
|
||||
cspot::TrackInfo trackInfo;
|
||||
|
||||
std::shared_ptr<cspot::LoginBlob> blob;
|
||||
std::unique_ptr<cspot::SpircHandler> spirc;
|
||||
@@ -206,11 +208,13 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
|
||||
trackStatus = TRACK_INIT;
|
||||
// memorize position for when track's beginning will be detected
|
||||
startOffset = std::get<int>(event->data);
|
||||
notify = !flushed;
|
||||
flushed = false;
|
||||
// Spotify servers do not send volume at connection
|
||||
spirc->setRemoteVolume(volume);
|
||||
|
||||
cmdHandler(CSPOT_START, 44100);
|
||||
CSPOT_LOG(info, "(re)start playing");
|
||||
CSPOT_LOG(info, "(re)start playing at %d", startOffset);
|
||||
break;
|
||||
}
|
||||
case cspot::SpircHandler::EventType::PLAY_PAUSE: {
|
||||
@@ -219,16 +223,14 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> event
|
||||
break;
|
||||
}
|
||||
case cspot::SpircHandler::EventType::TRACK_INFO: {
|
||||
auto trackInfo = std::get<cspot::TrackInfo>(event->data);
|
||||
cmdHandler(CSPOT_TRACK_INFO, trackInfo.duration, startOffset, trackInfo.artist.c_str(),
|
||||
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
|
||||
spirc->updatePositionMs(startOffset);
|
||||
startOffset = 0;
|
||||
trackInfo = std::get<cspot::TrackInfo>(event->data);
|
||||
break;
|
||||
}
|
||||
case cspot::SpircHandler::EventType::FLUSH:
|
||||
flushed = true;
|
||||
__attribute__ ((fallthrough));
|
||||
case cspot::SpircHandler::EventType::NEXT:
|
||||
case cspot::SpircHandler::EventType::PREV:
|
||||
case cspot::SpircHandler::EventType::FLUSH: {
|
||||
case cspot::SpircHandler::EventType::PREV: {
|
||||
cmdHandler(CSPOT_FLUSH);
|
||||
break;
|
||||
}
|
||||
@@ -411,8 +413,13 @@ void cspotPlayer::runTask() {
|
||||
uint32_t started;
|
||||
cmdHandler(CSPOT_QUERY_STARTED, &started);
|
||||
if (started) {
|
||||
CSPOT_LOG(info, "next track's audio has reached DAC");
|
||||
spirc->notifyAudioReachedPlayback();
|
||||
CSPOT_LOG(info, "next track's audio has reached DAC (offset %d)", startOffset);
|
||||
if (notify) spirc->notifyAudioReachedPlayback();
|
||||
else notify = true;
|
||||
cmdHandler(CSPOT_TRACK_INFO, trackInfo.duration, startOffset, trackInfo.artist.c_str(),
|
||||
trackInfo.album.c_str(), trackInfo.name.c_str(), trackInfo.imageUrl.c_str());
|
||||
spirc->updatePositionMs(startOffset);
|
||||
startOffset = 0;
|
||||
trackStatus = TRACK_STREAM;
|
||||
}
|
||||
} else if (trackStatus == TRACK_END) {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -227,7 +227,7 @@ int main(int argc, char *argv[]) {
|
||||
} else if (!strcasecmp(arg, "-i")) {
|
||||
identity = *++argv;
|
||||
} else {
|
||||
// nothing let's try to be smart and handle legacy crappy
|
||||
// nothing let's try to be smart and handle legacy crap
|
||||
if (!identity) identity = *argv;
|
||||
else if (!type) (void) !asprintf(&type, "%s.local", *argv);
|
||||
else if (!port) port = atoi(*argv);
|
||||
@@ -235,6 +235,7 @@ int main(int argc, char *argv[]) {
|
||||
txt = (const char**) malloc((argc + 1) * sizeof(char**));
|
||||
memcpy(txt, argv, argc * sizeof(char**));
|
||||
txt[argc] = NULL;
|
||||
break;
|
||||
}
|
||||
argc--;
|
||||
}
|
||||
@@ -250,13 +251,14 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
mdnsd_set_hostname(svr, hostname, host);
|
||||
svc = mdnsd_register_svc(svr, identity, type, port, NULL, txt);
|
||||
mdns_service_destroy(svc);
|
||||
// mdns_service_destroy(svc);
|
||||
|
||||
#ifdef _WIN32
|
||||
Sleep(INFINITE);
|
||||
#else
|
||||
pause();
|
||||
#endif
|
||||
mdns_service_remove(svr, svc);
|
||||
mdnsd_stop(svr);
|
||||
} else {
|
||||
printf("Can't start server");
|
||||
@@ -264,7 +266,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
free(type);
|
||||
free(txt);
|
||||
if (txt) free(txt);
|
||||
|
||||
#ifdef _WIN32
|
||||
winsock_close();
|
||||
|
||||
@@ -144,11 +144,10 @@ static int create_recv_sock(uint32_t host) {
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
on = sizeof(on);
|
||||
socklen_t len;
|
||||
if (!getsockopt(sd, SOL_SOCKET, SO_REUSEPORT,(char*) &on, &len)) {
|
||||
socklen_t len = sizeof(on);
|
||||
if (!getsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, &len)) {
|
||||
on = 1;
|
||||
if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT,(char*) &on, sizeof(on))) < 0) {
|
||||
if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on))) < 0) {
|
||||
log_message(LOG_ERR, "recv setsockopt(SO_REUSEPORT): %m\n", r);
|
||||
}
|
||||
}
|
||||
|
||||
0
components/spotify/cspot/bell/external/nanopb/generator/nanopb_generator.py
vendored
Normal file → Executable file
0
components/spotify/cspot/bell/external/nanopb/generator/nanopb_generator.py
vendored
Normal file → Executable file
0
components/spotify/cspot/bell/external/nanopb/generator/nanopb_generator.py2
vendored
Normal file → Executable file
0
components/spotify/cspot/bell/external/nanopb/generator/nanopb_generator.py2
vendored
Normal file → Executable file
0
components/spotify/cspot/bell/external/nanopb/generator/protoc
vendored
Normal file → Executable file
0
components/spotify/cspot/bell/external/nanopb/generator/protoc
vendored
Normal file → Executable file
0
components/spotify/cspot/bell/external/nanopb/generator/protoc-gen-nanopb
vendored
Normal file → Executable file
0
components/spotify/cspot/bell/external/nanopb/generator/protoc-gen-nanopb
vendored
Normal file → Executable file
@@ -5,6 +5,7 @@
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
#if __has_include("avahi-client/client.h")
|
||||
#include <avahi-client/client.h>
|
||||
|
||||
@@ -37,7 +37,6 @@ class TrackPlayer : bell::Task {
|
||||
typedef std::function<size_t(uint8_t*, size_t, std::string_view)>
|
||||
DataCallback;
|
||||
typedef std::function<void()> EOFCallback;
|
||||
typedef std::function<bool()> isAiringCallback;
|
||||
|
||||
TrackPlayer(std::shared_ptr<cspot::Context> ctx,
|
||||
std::shared_ptr<cspot::TrackQueue> trackQueue,
|
||||
|
||||
@@ -54,6 +54,7 @@ class QueuedTrack {
|
||||
|
||||
uint32_t requestedPosition;
|
||||
std::string identifier;
|
||||
bool loading = false;
|
||||
|
||||
// Will return nullptr if the track is not ready
|
||||
std::shared_ptr<cspot::CDNAudioFile> getAudioFile();
|
||||
@@ -100,7 +101,7 @@ class TrackQueue : public bell::Task {
|
||||
bool hasTracks();
|
||||
bool isFinished();
|
||||
bool skipTrack(SkipDirection dir, bool expectNotify = true);
|
||||
void updateTracks(uint32_t requestedPosition = 0, bool initial = false);
|
||||
bool updateTracks(uint32_t requestedPosition = 0, bool initial = false);
|
||||
TrackInfo getTrackInfo(std::string_view identifier);
|
||||
std::shared_ptr<QueuedTrack> consumeTrack(
|
||||
std::shared_ptr<QueuedTrack> prevSong, int& offset);
|
||||
|
||||
@@ -201,15 +201,24 @@ void SpircHandler::handleFrame(std::vector<uint8_t>& data) {
|
||||
break;
|
||||
}
|
||||
case MessageType_kMessageTypeReplace: {
|
||||
CSPOT_LOG(debug, "Got replace frame");
|
||||
CSPOT_LOG(debug, "Got replace frame %d",
|
||||
playbackState->remoteTracks.size());
|
||||
playbackState->syncWithRemote();
|
||||
|
||||
trackQueue->updateTracks(playbackState->remoteFrame.state.position_ms,
|
||||
false);
|
||||
// 1st track is the current one, but update the position
|
||||
bool cleared = trackQueue->updateTracks(
|
||||
playbackState->remoteFrame.state.position_ms +
|
||||
ctx->timeProvider->getSyncedTimestamp() -
|
||||
playbackState->innerFrame.state.position_measured_at,
|
||||
false);
|
||||
|
||||
this->notify();
|
||||
|
||||
sendEvent(EventType::FLUSH);
|
||||
trackPlayer->resetState();
|
||||
// need to re-load all if streaming track is completed
|
||||
if (cleared) {
|
||||
sendEvent(EventType::FLUSH);
|
||||
trackPlayer->resetState();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MessageType_kMessageTypeShuffle: {
|
||||
|
||||
@@ -201,6 +201,7 @@ void TrackPlayer::runTask() {
|
||||
}
|
||||
|
||||
eof = false;
|
||||
track->loading = true;
|
||||
|
||||
CSPOT_LOG(info, "Playing");
|
||||
|
||||
@@ -255,6 +256,7 @@ void TrackPlayer::runTask() {
|
||||
|
||||
// always move back to LOADING (ensure proper seeking after last track has been loaded)
|
||||
currentTrackStream = nullptr;
|
||||
track->loading = false;
|
||||
}
|
||||
|
||||
if (eof) {
|
||||
|
||||
@@ -587,18 +587,18 @@ bool TrackQueue::isFinished() {
|
||||
return currentTracksIndex >= currentTracks.size() - 1;
|
||||
}
|
||||
|
||||
void TrackQueue::updateTracks(uint32_t requestedPosition, bool initial) {
|
||||
bool TrackQueue::updateTracks(uint32_t requestedPosition, bool initial) {
|
||||
std::scoped_lock lock(tracksMutex);
|
||||
bool cleared = true;
|
||||
|
||||
// Copy requested track list
|
||||
currentTracks = playbackState->remoteTracks;
|
||||
currentTracksIndex = playbackState->innerFrame.state.playing_track_index;
|
||||
|
||||
if (initial) {
|
||||
// Clear preloaded tracks
|
||||
preloadedTracks.clear();
|
||||
|
||||
// Copy requested track list
|
||||
currentTracks = playbackState->remoteTracks;
|
||||
|
||||
currentTracksIndex = playbackState->innerFrame.state.playing_track_index;
|
||||
|
||||
if (currentTracksIndex < currentTracks.size()) {
|
||||
// Push a song on the preloaded queue
|
||||
queueNextTrack(0, requestedPosition);
|
||||
@@ -608,14 +608,25 @@ void TrackQueue::updateTracks(uint32_t requestedPosition, bool initial) {
|
||||
notifyPending = true;
|
||||
|
||||
playableSemaphore->give();
|
||||
} else if (preloadedTracks[0]->loading) {
|
||||
// try to not re-load track if we are still loading it
|
||||
|
||||
// remove everything except first track
|
||||
preloadedTracks.erase(preloadedTracks.begin() + 1, preloadedTracks.end());
|
||||
|
||||
// Push a song on the preloaded queue
|
||||
CSPOT_LOG(info, "Keeping current track %d", currentTracksIndex);
|
||||
queueNextTrack(1);
|
||||
|
||||
cleared = false;
|
||||
} else {
|
||||
// Clear preloaded tracks
|
||||
preloadedTracks.clear();
|
||||
|
||||
// Copy requested track list
|
||||
currentTracks = playbackState->remoteTracks;
|
||||
|
||||
// Push a song on the preloaded queue
|
||||
CSPOT_LOG(info, "Re-loading current track");
|
||||
queueNextTrack(0, requestedPosition);
|
||||
}
|
||||
|
||||
return cleared;
|
||||
}
|
||||
|
||||
@@ -288,6 +288,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
|
||||
output.frames_played = 0;
|
||||
output.external = DECODE_RAOP;
|
||||
output.state = OUTPUT_STOPPED;
|
||||
|
||||
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
|
||||
LOG_INFO("resizing buffer %u", outputbuf->size);
|
||||
break;
|
||||
@@ -377,6 +378,7 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
|
||||
output.state = OUTPUT_STOPPED;
|
||||
sink_state = SINK_ABORT;
|
||||
_buf_flush(outputbuf);
|
||||
_buf_limit(outputbuf, 0);
|
||||
if (decode.state != DECODE_STOPPED) decode.state = DECODE_ERROR;
|
||||
LOG_INFO("CSpot start track");
|
||||
break;
|
||||
|
||||
@@ -216,7 +216,7 @@ static EXT_RAM_ATTR struct {
|
||||
|
||||
static EXT_RAM_ATTR struct {
|
||||
int mode;
|
||||
int n, style, max;
|
||||
int n, style, max, gain;
|
||||
u16_t config;
|
||||
struct bar_s bars[MAX_BARS] ;
|
||||
} led_visu;
|
||||
@@ -1089,10 +1089,10 @@ static void displayer_update(void) {
|
||||
if (led_display && led_visu.mode) {
|
||||
// run built in visualizer effects
|
||||
if (led_visu.mode == VISU_VUMETER) {
|
||||
vu_scale(led_visu.bars, led_visu.max, meters.levels);
|
||||
vu_scale(led_visu.bars, led_visu.gain, meters.levels);
|
||||
led_vu_display(led_visu.bars[0].current, led_visu.bars[1].current, led_visu.max, led_visu.style);
|
||||
} else if (led_visu.mode == VISU_SPECTRUM) {
|
||||
spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples);
|
||||
spectrum_scale(led_visu.n, led_visu.bars, led_visu.gain, meters.samples);
|
||||
uint8_t* p = (uint8_t*) led_data;
|
||||
for (int i = 0; i < led_visu.n; i++) {
|
||||
*p = led_visu.bars[i].current;
|
||||
@@ -1100,7 +1100,7 @@ static void displayer_update(void) {
|
||||
}
|
||||
led_vu_spectrum(led_data, led_visu.max, led_visu.n, led_visu.style);
|
||||
} else if (led_visu.mode == VISU_WAVEFORM) {
|
||||
spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples);
|
||||
spectrum_scale(led_visu.n, led_visu.bars, led_visu.gain, meters.samples);
|
||||
led_vu_spin_dial(
|
||||
led_visu.bars[led_visu.n-2].current,
|
||||
led_visu.bars[(led_visu.n/2)+1].current * 50 / led_visu.max,
|
||||
@@ -1277,8 +1277,8 @@ static void ledv_handler( u8_t *data, int len) {
|
||||
led_visu.mode = pkt->which;
|
||||
led_visu.style = pkt->style;
|
||||
led_visu.max = pkt->bright;
|
||||
led_visu.gain = led_visu.max * led_vu_scale() / 100;
|
||||
|
||||
led_vu_clear();
|
||||
if (led_visu.mode) {
|
||||
if (led_visu.mode == VISU_SPECTRUM) {
|
||||
led_visu.n = (led_visu.config < MAX_BARS) ? led_visu.config : MAX_BARS;
|
||||
@@ -1293,8 +1293,10 @@ static void ledv_handler( u8_t *data, int len) {
|
||||
// reset bars maximum
|
||||
for (int i = led_visu.n; --i >= 0;) led_visu.bars[i].max = 0;
|
||||
|
||||
LOG_INFO("LED Visualizer mode %u with bars:%u max:%u style:%d", led_visu.mode, led_visu.n, led_visu.max, led_visu.style);
|
||||
LOG_INFO("LED Visualizer mode %u with bars:%u max:%u style:%d gain:%u", led_visu.mode, led_visu.n, led_visu.max, led_visu.style, led_visu.gain);
|
||||
} else {
|
||||
led_vu_clear();
|
||||
|
||||
LOG_INFO("Stopping led visualizer");
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config, bool
|
||||
char *p;
|
||||
|
||||
i2c_addr = adac_init(config, i2c_port_num);
|
||||
if (!i2c_addr) return true;
|
||||
|
||||
ESP_LOGI(TAG, "DAC on I2C @%d", i2c_addr);
|
||||
|
||||
@@ -137,6 +136,7 @@ bool i2c_json_execute(char *set) {
|
||||
if ((action = cJSON_GetObjectItemCaseSensitive(item, "gpio")) != NULL) {
|
||||
cJSON *level = cJSON_GetObjectItemCaseSensitive(item, "level");
|
||||
ESP_LOGI(TAG, "set GPIO %d at %d", action->valueint, level->valueint);
|
||||
if (action->valueint < GPIO_NUM_MAX) gpio_pad_select_gpio(action->valueint);
|
||||
gpio_set_direction_x(action->valueint, GPIO_MODE_OUTPUT);
|
||||
gpio_set_level_x(action->valueint, level->valueint);
|
||||
continue;
|
||||
|
||||
@@ -121,6 +121,9 @@ static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder,
|
||||
_buf_inc_readp(streambuf, bytes);
|
||||
UNLOCK_S;
|
||||
|
||||
// give some time for stream to acquire data otherwise flac will hammer us
|
||||
if (!end && !bytes) usleep(100 * 1000);
|
||||
|
||||
*want = bytes;
|
||||
|
||||
return end ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
#define MAX_OPUS_FRAMES 5760
|
||||
|
||||
struct opus {
|
||||
enum {OGG_SYNC, OGG_ID_HEADER, OGG_COMMENT_HEADER} status;
|
||||
enum { OGG_ID_HEADER, OGG_COMMENT_HEADER } status;
|
||||
ogg_stream_state state;
|
||||
ogg_packet packet;
|
||||
ogg_sync_state sync;
|
||||
@@ -131,95 +131,109 @@ static opus_uint32 parse_uint32(const unsigned char* _data) {
|
||||
(opus_uint32)_data[2] << 16 | (opus_uint32)_data[3] << 24;
|
||||
}
|
||||
|
||||
static int get_opus_packet(void) {
|
||||
static int get_audio_packet(void) {
|
||||
int status, packet = -1;
|
||||
|
||||
LOCK_S;
|
||||
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
|
||||
|
||||
|
||||
while (!(status = OG(&go, stream_packetout, &u->state, &u->packet)) && bytes) {
|
||||
|
||||
// if sync_pageout (or sync_pageseek) is not called here, sync builds ups
|
||||
while (!(status = OG(&go, sync_pageout, &u->sync, &u->page)) && bytes) {
|
||||
|
||||
// if sync_pageout (or sync_pageseek) is not called here, sync buffers build up
|
||||
while (!(status = OG(&go, sync_pageout, &u->sync, &u->page)) && bytes) {
|
||||
size_t consumed = min(bytes, 4096);
|
||||
char* buffer = OG(&gu, sync_buffer, &u->sync, consumed);
|
||||
char* buffer = OG(&go, sync_buffer, &u->sync, consumed);
|
||||
memcpy(buffer, streambuf->readp, consumed);
|
||||
OG(&gu, sync_wrote, &u->sync, consumed);
|
||||
OG(&go, sync_wrote, &u->sync, consumed);
|
||||
|
||||
_buf_inc_readp(streambuf, consumed);
|
||||
bytes -= consumed;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have a new page, put it in
|
||||
if (status) OG(&go, stream_pagein, &u->state, &u->page);
|
||||
}
|
||||
|
||||
// only return a negative value when true end of streaming is reached
|
||||
if (status > 0) packet = status;
|
||||
else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
|
||||
// if we have a new page, put it in and reset serialno at BoS
|
||||
if (status) {
|
||||
OG(&go, stream_pagein, &u->state, &u->page);
|
||||
if (OG(&go, page_bos, &u->page)) OG(&go, stream_reset_serialno, &u->state, OG(&go, page_serialno, &u->page));
|
||||
}
|
||||
}
|
||||
|
||||
/* discard header packets. With no packet, we return a negative value
|
||||
* when there is really nothing more to proceed */
|
||||
if (status > 0 && memcmp(u->packet.packet, "OpusHead", 8) && memcmp(u->packet.packet, "OpusTags", 8)) packet = status;
|
||||
else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
|
||||
|
||||
UNLOCK_S;
|
||||
return packet;
|
||||
}
|
||||
|
||||
static int read_opus_header(void) {
|
||||
int status = 0;
|
||||
bool fetch = true;
|
||||
int done = 0;
|
||||
bool fetch = true;
|
||||
|
||||
LOCK_S;
|
||||
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
|
||||
|
||||
while (bytes && !status) {
|
||||
// first fetch a page if we need one
|
||||
if (fetch) {
|
||||
while (bytes && !done) {
|
||||
int status;
|
||||
|
||||
// get aligned to a page and ready to bring it in
|
||||
do {
|
||||
size_t consumed = min(bytes, 4096);
|
||||
char* buffer = OG(&gu, sync_buffer, &u->sync, consumed);
|
||||
|
||||
char* buffer = OG(&go, sync_buffer, &u->sync, consumed);
|
||||
memcpy(buffer, streambuf->readp, consumed);
|
||||
OG(&gu, sync_wrote, &u->sync, consumed);
|
||||
OG(&go, sync_wrote, &u->sync, consumed);
|
||||
|
||||
_buf_inc_readp(streambuf, consumed);
|
||||
bytes -= consumed;
|
||||
|
||||
if (!OG(&gu, sync_pageseek, &u->sync, &u->page)) continue;
|
||||
}
|
||||
status = fetch ? OG(&go, sync_pageout, &u->sync, &u->page) :
|
||||
OG(&go, sync_pageseek, &u->sync, &u->page);
|
||||
} while (bytes && status <= 0);
|
||||
|
||||
switch (u->status) {
|
||||
case OGG_SYNC:
|
||||
u->status = OGG_ID_HEADER;
|
||||
OG(&gu, stream_init, &u->state, OG(&gu, page_serialno, &u->page));
|
||||
fetch = false;
|
||||
break;
|
||||
case OGG_ID_HEADER:
|
||||
status = OG(&gu, stream_pagein, &u->state, &u->page);
|
||||
if (OG(&gu, stream_packetout, &u->state, &u->packet)) {
|
||||
if (u->packet.bytes < 19 || memcmp(u->packet.packet, "OpusHead", 8)) {
|
||||
LOG_ERROR("wrong opus header packet (size:%u)", u->packet.bytes);
|
||||
status = -100;
|
||||
break;
|
||||
}
|
||||
u->status = OGG_COMMENT_HEADER;
|
||||
// nothing has been found and we have no more bytes, come back later
|
||||
if (status <= 0) break;
|
||||
|
||||
// always set stream serialno if we have a new one
|
||||
if (OG(&go, page_bos, &u->page)) OG(&go, stream_reset_serialno, &u->state, OG(&go, page_serialno, &u->page));
|
||||
|
||||
// bring new page in if we want it (otherwise we're just skipping)
|
||||
if (fetch) OG(&go, stream_pagein, &u->state, &u->page);
|
||||
|
||||
// no need for a switch...case
|
||||
if (u->status == OGG_ID_HEADER) {
|
||||
// we need the id packet, get more pages if we don't
|
||||
if (OG(&go, stream_packetout, &u->state, &u->packet) <= 0) continue;
|
||||
|
||||
// make sure this is a valid packet
|
||||
if (u->packet.bytes < 19 || memcmp(u->packet.packet, "OpusHead", 8)) {
|
||||
LOG_ERROR("wrong header packet (size:%u)", u->packet.bytes);
|
||||
done = -100;
|
||||
} else {
|
||||
u->status = OGG_COMMENT_HEADER;
|
||||
u->channels = u->packet.packet[9];
|
||||
u->pre_skip = parse_uint16(u->packet.packet + 10);
|
||||
u->rate = parse_uint32(u->packet.packet + 12);
|
||||
u->gain = parse_int16(u->packet.packet + 16);
|
||||
u->decoder = OP(&gu, decoder_create, 48000, u->channels, &status);
|
||||
fetch = false;
|
||||
if (!u->decoder || status != OPUS_OK) {
|
||||
LOG_ERROR("can't create decoder %d (channels:%u)", status, u->channels);
|
||||
}
|
||||
else {
|
||||
LOG_INFO("codec up and running");
|
||||
}
|
||||
}
|
||||
fetch = true;
|
||||
break;
|
||||
case OGG_COMMENT_HEADER:
|
||||
// skip packets to consume VorbisComment. With opus, header packets align on pages
|
||||
status = OG(&gu, page_packets, &u->page);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} else if (u->status == OGG_COMMENT_HEADER) {
|
||||
// don't consume VorbisComment which could be a huge packet, just skip it
|
||||
if (!OG(&go, page_packets, &u->page)) continue;
|
||||
LOG_INFO("comment skipped successfully");
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
UNLOCK_S;
|
||||
return status;
|
||||
return done;
|
||||
}
|
||||
|
||||
static decode_state opus_decompress(void) {
|
||||
@@ -271,7 +285,7 @@ static decode_state opus_decompress(void) {
|
||||
memcpy(write_buf, u->overbuf, u->overframes * BYTES_PER_FRAME);
|
||||
n = u->overframes;
|
||||
u->overframes = 0;
|
||||
} else if ((packet = get_opus_packet()) > 0) {
|
||||
} else if ((packet = get_audio_packet()) > 0) {
|
||||
if (frames < MAX_OPUS_FRAMES) {
|
||||
// don't have enough contiguous space, use the overflow buffer
|
||||
n = OP(&gu, decode, u->decoder, u->packet.packet, u->packet.bytes, (opus_int16*) u->overbuf, MAX_OPUS_FRAMES, 0);
|
||||
@@ -286,7 +300,7 @@ static decode_state opus_decompress(void) {
|
||||
* outputbuf and streambuf for maybe a long time while we process it all, so don't do that */
|
||||
n = OP(&gu, decode, u->decoder, u->packet.packet, u->packet.bytes, (opus_int16*) write_buf, frames, 0);
|
||||
}
|
||||
} else if (!packet && !OG(&go, page_eos, &u->page)) {
|
||||
} else if (!packet) {
|
||||
UNLOCK_O_direct;
|
||||
return DECODE_RUNNING;
|
||||
}
|
||||
@@ -342,7 +356,7 @@ static decode_state opus_decompress(void) {
|
||||
|
||||
} else {
|
||||
|
||||
LOG_INFO("opus decode error: %d", n);
|
||||
LOG_INFO("decode error: %d", n);
|
||||
UNLOCK_O_direct;
|
||||
return DECODE_COMPLETE;
|
||||
}
|
||||
@@ -357,12 +371,12 @@ static void opus_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
|
||||
|
||||
if (!u->overbuf) u->overbuf = malloc(MAX_OPUS_FRAMES * BYTES_PER_FRAME);
|
||||
|
||||
u->status = OGG_SYNC;
|
||||
u->status = OGG_ID_HEADER;
|
||||
u->overframes = 0;
|
||||
|
||||
OG(&gu, sync_clear, &u->sync);
|
||||
OG(&gu, stream_clear, &u->state);
|
||||
OG(&gu, stream_init, &u->state, -1);
|
||||
|
||||
OG(&go, stream_clear, &u->state);
|
||||
OG(&go, sync_clear, &u->sync);
|
||||
OG(&go, stream_init, &u->state, -1);
|
||||
}
|
||||
|
||||
static void opus_close(void) {
|
||||
@@ -372,8 +386,8 @@ static void opus_close(void) {
|
||||
free(u->overbuf);
|
||||
u->overbuf = NULL;
|
||||
|
||||
OG(&gu, stream_clear, &u->state);
|
||||
OG(&gu, sync_clear, &u->sync);
|
||||
OG(&go, stream_clear, &u->state);
|
||||
OG(&go, sync_clear, &u->sync);
|
||||
}
|
||||
|
||||
static bool load_opus(void) {
|
||||
@@ -394,7 +408,7 @@ static bool load_opus(void) {
|
||||
}
|
||||
|
||||
g_handle->ogg_stream_clear = dlsym(g_handle->handle, "ogg_stream_clear");
|
||||
g_handle->.ogg_stream_reset = dlsym(g_handle->handle, "ogg_stream_reset");
|
||||
g_handle->ogg_stream_reset = dlsym(g_handle->handle, "ogg_stream_reset");
|
||||
g_handle->ogg_stream_eos = dlsym(g_handle->handle, "ogg_stream_eos");
|
||||
g_handle->ogg_stream_reset_serialno = dlsym(g_handle->handle, "ogg_stream_reset_serialno");
|
||||
g_handle->ogg_sync_clear = dlsym(g_handle->handle, "ogg_sync_clear");
|
||||
|
||||
@@ -443,7 +443,7 @@ void output_close_common(void) {
|
||||
}
|
||||
|
||||
void output_flush(void) {
|
||||
LOG_INFO("flush output buffer");
|
||||
LOG_INFO("flush output buffer (full)");
|
||||
buf_flush(outputbuf);
|
||||
LOCK;
|
||||
output.fade = FADE_INACTIVE;
|
||||
@@ -457,3 +457,15 @@ void output_flush(void) {
|
||||
output.frames_played = 0;
|
||||
UNLOCK;
|
||||
}
|
||||
|
||||
bool output_flush_streaming(void) {
|
||||
LOG_INFO("flush output buffer (streaming)");
|
||||
LOCK;
|
||||
bool flushed = output.track_start != NULL;
|
||||
if (output.track_start) {
|
||||
outputbuf->writep = output.track_start;
|
||||
output.track_start = NULL;
|
||||
}
|
||||
UNLOCK;
|
||||
return flushed;
|
||||
}
|
||||
|
||||
@@ -139,8 +139,11 @@ static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t g
|
||||
u8_t *buf = silencebuf;
|
||||
memcpy(btout + oframes * BYTES_PER_FRAME, buf, out_frames * BYTES_PER_FRAME);
|
||||
}
|
||||
|
||||
output_visu_export(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
|
||||
|
||||
// don't update visu if we don't have enough data in buffer (500 ms)
|
||||
if (silence || _buf_used(outputbuf) > BYTES_PER_FRAME * output.current_sample_rate / 2) {
|
||||
output_visu_export(btout + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
|
||||
}
|
||||
|
||||
oframes += out_frames;
|
||||
|
||||
|
||||
@@ -485,8 +485,8 @@ static int _i2s_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32
|
||||
memcpy(obuf + oframes * BYTES_PER_FRAME, silencebuf, out_frames * BYTES_PER_FRAME);
|
||||
}
|
||||
|
||||
// don't update visu if we don't have enough data in buffer
|
||||
if (silence || output.external || _buf_used(outputbuf) > outputbuf->size >> 2 ) {
|
||||
// don't update visu if we don't have enough data in buffer (500 ms)
|
||||
if (silence || _buf_used(outputbuf) > BYTES_PER_FRAME * output.current_sample_rate / 2) {
|
||||
output_visu_export(obuf + oframes * BYTES_PER_FRAME, out_frames, output.current_sample_rate, silence, (gainL + gainR) / 2);
|
||||
}
|
||||
|
||||
@@ -744,7 +744,7 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst) {
|
||||
|
||||
// we assume frame == 0 as well...
|
||||
if (!src) {
|
||||
count = 192;
|
||||
count = 0;
|
||||
vu = VUCP24[0];
|
||||
}
|
||||
|
||||
@@ -757,7 +757,7 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst) {
|
||||
|
||||
if (!count--) {
|
||||
*dst++ = (vu << 24) | (PREAMBLE_B << 16) | 0xCCCC;
|
||||
count = 192;
|
||||
count = 191;
|
||||
} else {
|
||||
*dst++ = (vu << 24) | (PREAMBLE_M << 16) | 0xCCCC;
|
||||
}
|
||||
@@ -771,7 +771,7 @@ static void IRAM_ATTR spdif_convert(ISAMPLE_T *src, size_t frames, u32_t *dst) {
|
||||
|
||||
if (!count--) {
|
||||
*dst++ = (vu << 24) | (PREAMBLE_B << 16) | aux;
|
||||
count = 192;
|
||||
count = 191;
|
||||
} else {
|
||||
*dst++ = (vu << 24) | (PREAMBLE_M << 16) | aux;
|
||||
}
|
||||
|
||||
@@ -305,8 +305,18 @@ static void process_strm(u8_t *pkt, int len) {
|
||||
sendSTAT("STMt", strm->replay_gain); // STMt replay_gain is no longer used to track latency, but support it
|
||||
break;
|
||||
case 'f':
|
||||
{
|
||||
decode_flush(false);
|
||||
bool flushed = false;
|
||||
if (!output.external) flushed |= output_flush_streaming();
|
||||
// we can have fully finished the current streaming, that's still a flush
|
||||
if (stream_disconnect() || flushed) sendSTAT("STMf", 0);
|
||||
buf_flush(streambuf);
|
||||
output.stop_time = gettime_ms();
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
decode_flush(strm->command == 'q');
|
||||
decode_flush(true);
|
||||
if (!output.external) output_flush();
|
||||
status.frames_played = 0;
|
||||
if (stream_disconnect() && strm->command == 'f') sendSTAT("STMf", 0);
|
||||
@@ -383,7 +393,9 @@ static void process_strm(u8_t *pkt, int len) {
|
||||
stream_file(header, header_len, strm->threshold * 1024);
|
||||
autostart -= 2;
|
||||
} else {
|
||||
stream_sock(ip, port, header, header_len, strm->threshold * 1024, autostart >= 2);
|
||||
stream_sock(ip, port, strm->flags & 0x20,
|
||||
strm->format == 'o' || strm->format == 'u' || (strm->format == 'f' && strm->pcm_sample_size == 'o'),
|
||||
header, header_len, strm->threshold * 1024, autostart >= 2);
|
||||
}
|
||||
sendSTAT("STMc", 0);
|
||||
sentSTMu = sentSTMo = sentSTMl = false;
|
||||
@@ -982,8 +994,8 @@ void slimproto(log_level level, char *server, u8_t mac[6], const char *name, con
|
||||
// in embedded we give up after a while no matter what
|
||||
if (++failed_connect > MAX_SERVER_RETRIES && !server) {
|
||||
slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL, MAX_SERVER_RETRIES);
|
||||
if (!slimproto_ip) return;
|
||||
} else if (reconnect && MAX_SERVER_RETRIES && failed_connect > 5 * MAX_SERVER_RETRIES) return;
|
||||
if (!slimproto_ip && !output.external) return;
|
||||
} else if (reconnect && MAX_SERVER_RETRIES && failed_connect > 5 * MAX_SERVER_RETRIES && !output.external) return;
|
||||
#else
|
||||
// rediscover server if it was not set at startup or exit
|
||||
if (!server && ++failed_connect > 5) {
|
||||
|
||||
@@ -585,7 +585,7 @@ struct streamstate {
|
||||
void stream_init(log_level level, unsigned stream_buf_size);
|
||||
void stream_close(void);
|
||||
void stream_file(const char *header, size_t header_len, unsigned threshold);
|
||||
void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
|
||||
void stream_sock(u32_t ip, u16_t port, bool use_ssl, bool use_ogg, const char *header, size_t header_len, unsigned threshold, bool cont_wait);
|
||||
bool stream_disconnect(void);
|
||||
|
||||
// decode.c
|
||||
@@ -727,6 +727,7 @@ struct outputstate {
|
||||
void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[], unsigned idle);
|
||||
void output_close_common(void);
|
||||
void output_flush(void);
|
||||
bool output_flush_streaming(void);
|
||||
// _* called with mutex locked
|
||||
frames_t _output_frames(frames_t avail);
|
||||
void _checkfade(bool);
|
||||
|
||||
@@ -59,7 +59,24 @@ is enough and much faster than a mutex
|
||||
static bool polling;
|
||||
static sockfd fd;
|
||||
|
||||
struct streamstate stream;
|
||||
struct EXT_RAM_ATTR streamstate stream;
|
||||
|
||||
static EXT_RAM_ATTR struct {
|
||||
bool flac;
|
||||
enum { OGG_OFF, OGG_SYNC, OGG_HEADER, OGG_SEGMENTS, OGG_PAGE } state;
|
||||
size_t want, miss, match;
|
||||
u64_t granule;
|
||||
u8_t* data, segments[255];
|
||||
#pragma pack(push, 1)
|
||||
struct {
|
||||
char pattern[4];
|
||||
u8_t version, type;
|
||||
u64_t granule;
|
||||
u32_t serial, page, checksum;
|
||||
u8_t count;
|
||||
} header;
|
||||
#pragma pack(pop)
|
||||
} ogg;
|
||||
|
||||
#if USE_SSL
|
||||
static SSL_CTX *SSLctx;
|
||||
@@ -113,7 +130,6 @@ static int _poll(SSL *ssl, struct pollfd *pollinfo, int timeout) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static bool send_header(void) {
|
||||
char *ptr = stream.header;
|
||||
int len = stream.header_len;
|
||||
@@ -148,6 +164,8 @@ static bool running = true;
|
||||
static void _disconnect(stream_state state, disconnect_code disconnect) {
|
||||
stream.state = state;
|
||||
stream.disconnect = disconnect;
|
||||
if (ogg.state == OGG_PAGE && ogg.data) free(ogg.data);
|
||||
ogg.data = NULL;
|
||||
#if USE_SSL
|
||||
if (ssl) {
|
||||
SSL_shutdown(ssl);
|
||||
@@ -160,6 +178,135 @@ static void _disconnect(stream_state state, disconnect_code disconnect) {
|
||||
wake_controller();
|
||||
}
|
||||
|
||||
static size_t memfind(const u8_t* haystack, size_t n, const char* needle, size_t len, size_t* offset) {
|
||||
size_t i;
|
||||
for (i = 0; i < n && *offset != len; i++) *offset = (haystack[i] == needle[*offset]) ? *offset + 1 : 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
/* https://xiph.org/ogg/doc/framing.html
|
||||
* https://xiph.org/flac/ogg_mapping.html
|
||||
* https://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-610004.2 */
|
||||
|
||||
static void stream_ogg(size_t n) {
|
||||
if (ogg.state == OGG_OFF) return;
|
||||
u8_t* p = streambuf->writep;
|
||||
|
||||
while (n) {
|
||||
size_t consumed = min(ogg.miss, n);
|
||||
|
||||
// copy as many bytes as possible and come back later if we do'nt have enough
|
||||
if (ogg.data) {
|
||||
memcpy(ogg.data + ogg.want - ogg.miss, p, consumed);
|
||||
ogg.miss -= consumed;
|
||||
if (ogg.miss) return;
|
||||
}
|
||||
|
||||
// we have what we want, let's parse
|
||||
switch (ogg.state) {
|
||||
case OGG_SYNC: {
|
||||
ogg.miss -= consumed;
|
||||
if (consumed) break;
|
||||
|
||||
// we have to memorize position in case any of last 3 bytes match...
|
||||
size_t pos = memfind(p, n, "OggS", 4, &ogg.match);
|
||||
if (ogg.match == 4) {
|
||||
consumed = pos - ogg.match;
|
||||
ogg.state = OGG_HEADER;
|
||||
ogg.miss = ogg.want = sizeof(ogg.header);
|
||||
ogg.data = (u8_t*) &ogg.header;
|
||||
ogg.match = 0;
|
||||
} else {
|
||||
if (!ogg.match) LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
|
||||
LOG_INFO("OggS not at expected position %zu/%zu", pos, n);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OGG_HEADER:
|
||||
if (!memcmp(ogg.header.pattern, "OggS", 4)) {
|
||||
ogg.miss = ogg.want = ogg.header.count;
|
||||
ogg.data = ogg.segments;
|
||||
ogg.state = OGG_SEGMENTS;
|
||||
} else {
|
||||
ogg.state = OGG_SYNC;
|
||||
ogg.data = NULL;
|
||||
}
|
||||
break;
|
||||
case OGG_SEGMENTS:
|
||||
// calculate size of page using lacing values
|
||||
for (size_t i = 0; i < ogg.want; i++) ogg.miss += ogg.data[i];
|
||||
ogg.want = ogg.miss;
|
||||
|
||||
if (ogg.header.granule == 0 || (ogg.header.granule == -1 && ogg.granule == 0)) {
|
||||
// granule 0 means a new stream, so let's look into it
|
||||
ogg.state = OGG_PAGE;
|
||||
ogg.data = malloc(ogg.want);
|
||||
} else {
|
||||
// otherwise, jump over data
|
||||
ogg.state = OGG_SYNC;
|
||||
ogg.data = NULL;
|
||||
}
|
||||
|
||||
// memorize granule for next page
|
||||
if (ogg.header.granule != -1) ogg.granule = ogg.header.granule;
|
||||
break;
|
||||
case OGG_PAGE: {
|
||||
char** tag = (char* []){ "\x3vorbis", "OpusTags", NULL };
|
||||
size_t ofs = 0;
|
||||
|
||||
/* with OggFlac, we need the next page (packet) - VorbisComment is wrapped into a FLAC_METADATA
|
||||
* and except with vorbis, comment packet starts a new page but even in vorbis, it won't span
|
||||
* accross multiple pages */
|
||||
if (ogg.flac) ofs = 4;
|
||||
else if (!memcmp(ogg.data, "\x7f""FLAC", 5)) ogg.flac = true;
|
||||
else for (size_t n = 0; *tag; tag++, ofs = 0) if ((ofs = memfind(ogg.data, ogg.want, *tag, strlen(*tag), &n)) && n == strlen(*tag)) break;
|
||||
|
||||
if (ofs) {
|
||||
// u32:len,char[]:vendorId, u32:N, N x (u32:len,char[]:comment)
|
||||
char* p = (char*) ogg.data + ofs;
|
||||
p += *p + 4;
|
||||
u32_t count = *p;
|
||||
p += 4;
|
||||
|
||||
// LMS metadata format for Ogg is "Ogg", N x (u16:len,char[]:comment)
|
||||
memcpy(stream.header, "Ogg", 3);
|
||||
stream.header_len = 3;
|
||||
|
||||
for (u32_t len; count--; p += len) {
|
||||
len = *p;
|
||||
p += 4;
|
||||
|
||||
// only report what we use and don't overflow (network byte order)
|
||||
if (!strncasecmp(p, "TITLE=", 6) || !strncasecmp(p, "ARTIST=", 7) || !strncasecmp(p, "ALBUM=", 6)) {
|
||||
if (stream.header_len + len > MAX_HEADER) break;
|
||||
stream.header[stream.header_len++] = len >> 8;
|
||||
stream.header[stream.header_len++] = len;
|
||||
memcpy(stream.header + stream.header_len, p, len);
|
||||
stream.header_len += len;
|
||||
LOG_INFO("metadata: %.*s", len, p);
|
||||
}
|
||||
}
|
||||
|
||||
ogg.flac = false;
|
||||
stream.meta_send = true;
|
||||
wake_controller();
|
||||
LOG_INFO("Ogg metadata length: %u", stream.header_len - 3);
|
||||
}
|
||||
free(ogg.data);
|
||||
ogg.data = NULL;
|
||||
ogg.state = OGG_SYNC;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
p += consumed;
|
||||
n -= consumed;
|
||||
}
|
||||
}
|
||||
|
||||
static void *stream_thread() {
|
||||
|
||||
while (running) {
|
||||
@@ -343,6 +490,7 @@ static void *stream_thread() {
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
stream_ogg(n);
|
||||
_buf_inc_writep(streambuf, n);
|
||||
stream.bytes += n;
|
||||
if (stream.meta_interval) {
|
||||
@@ -485,7 +633,7 @@ void stream_file(const char *header, size_t header_len, unsigned threshold) {
|
||||
UNLOCK;
|
||||
}
|
||||
|
||||
void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
|
||||
void stream_sock(u32_t ip, u16_t port, bool use_ssl, bool use_ogg, const char *header, size_t header_len, unsigned threshold, bool cont_wait) {
|
||||
struct sockaddr_in addr;
|
||||
|
||||
#if EMBEDDED
|
||||
@@ -584,6 +732,10 @@ void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, un
|
||||
stream.sent_headers = false;
|
||||
stream.bytes = 0;
|
||||
stream.threshold = threshold;
|
||||
|
||||
ogg.miss = ogg.match = 0;
|
||||
ogg.state = use_ogg ? OGG_SYNC : OGG_OFF;
|
||||
ogg.flac = false;
|
||||
|
||||
UNLOCK;
|
||||
}
|
||||
@@ -604,6 +756,8 @@ bool stream_disconnect(void) {
|
||||
disc = true;
|
||||
}
|
||||
stream.state = STOPPED;
|
||||
if (ogg.state == OGG_PAGE && ogg.data) free(ogg.data);
|
||||
ogg.data = NULL;
|
||||
UNLOCK;
|
||||
return disc;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ static inline int32_t clip15(int32_t x) {
|
||||
|
||||
struct vorbis {
|
||||
bool opened;
|
||||
enum { OGG_SYNC, OGG_ID_HEADER, OGG_COMMENT_HEADER, OGG_SETUP_HEADER } status;
|
||||
enum { OGG_ID_HEADER, OGG_COMMENT_HEADER, OGG_SETUP_HEADER } status;
|
||||
struct {
|
||||
ogg_stream_state state;
|
||||
ogg_packet packet;
|
||||
@@ -72,6 +72,13 @@ static struct vorbis {
|
||||
// vorbis symbols to be dynamically loaded - from either vorbisfile or vorbisidec (tremor) version of library
|
||||
vorbis_info *(* ov_info)(OggVorbis_File *vf, int link);
|
||||
int (* ov_clear)(OggVorbis_File *vf);
|
||||
void (* ov_info_init)(vorbis_info* vi);
|
||||
void (* ov_info_clear)(vorbis_info* vi);
|
||||
void (* ov_comment_init)(vorbis_comment* vc);
|
||||
void (* ov_comment_clear(vorbis_comment *vc);
|
||||
int (* ov_block_init)(vorbis_dsp_state* v, vorbis_block* vb);
|
||||
int (* ov_block_clear)(vorbis_block* vb);
|
||||
void (* ov_vorbis_dsp_clear)(vorbis_dsp_state* v);
|
||||
long (* ov_read)(OggVorbis_File *vf, char *buffer, int length, int bigendianp, int word, int sgned, int *bitstream);
|
||||
long (* ov_read_tremor)(OggVorbis_File *vf, char *buffer, int length, int *bitstream);
|
||||
int (* ov_open_callbacks)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks);
|
||||
@@ -131,48 +138,16 @@ extern struct processstate process;
|
||||
#define OG(h, fn, ...) (h)->ogg_ ## fn(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
static int get_ogg_packet(void) {
|
||||
static int get_audio_packet(void) {
|
||||
int status, packet = -1;
|
||||
|
||||
LOCK_S;
|
||||
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
|
||||
|
||||
while (!(status = OG(&go, stream_packetout, &v->state, &v->packet)) && bytes) {
|
||||
|
||||
// if sync_pageout (or sync_pageseek) is not called first, sync buffers build ups
|
||||
while (!(status = OG(&go, sync_pageout, &v->sync, &v->page)) && bytes) {
|
||||
size_t consumed = min(bytes, 4096);
|
||||
char* buffer = OG(&gv, sync_buffer, &v->sync, consumed);
|
||||
memcpy(buffer, streambuf->readp, consumed);
|
||||
OG(&gv, sync_wrote, &v->sync, consumed);
|
||||
|
||||
_buf_inc_readp(streambuf, consumed);
|
||||
bytes -= consumed;
|
||||
}
|
||||
|
||||
// if we have a new page, put it in
|
||||
if (status) OG(&go, stream_pagein, &v->state, &v->page);
|
||||
}
|
||||
|
||||
// only return a negative value when true end of streaming is reached
|
||||
if (status > 0) packet = status;
|
||||
else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
|
||||
|
||||
UNLOCK_S;
|
||||
return packet;
|
||||
}
|
||||
|
||||
static int read_vorbis_header(void) {
|
||||
int status = 0;
|
||||
bool fetch = true;
|
||||
|
||||
LOCK_S;
|
||||
|
||||
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
|
||||
|
||||
while (bytes && !status) {
|
||||
// first fetch a page if we need one
|
||||
if (fetch) {
|
||||
|
||||
// if sync_pageout (or sync_pageseek) is not called here, sync buffers build up
|
||||
while (!(status = OG(&go, sync_pageout, &v->sync, &v->page)) && bytes) {
|
||||
size_t consumed = min(bytes, 4096);
|
||||
char* buffer = OG(&go, sync_buffer, &v->sync, consumed);
|
||||
memcpy(buffer, streambuf->readp, consumed);
|
||||
@@ -180,81 +155,122 @@ static int read_vorbis_header(void) {
|
||||
|
||||
_buf_inc_readp(streambuf, consumed);
|
||||
bytes -= consumed;
|
||||
|
||||
if (!OG(&go, sync_pageseek, &v->sync, &v->page)) continue;
|
||||
}
|
||||
|
||||
switch (v->status) {
|
||||
case OGG_SYNC:
|
||||
v->status = OGG_ID_HEADER;
|
||||
OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page));
|
||||
fetch = false;
|
||||
break;
|
||||
case OGG_ID_HEADER:
|
||||
status = OG(&go, stream_pagein, &v->state, &v->page);
|
||||
if (!OG(&go, stream_packetout, &v->state, &v->packet)) break;
|
||||
|
||||
// if we have a new page, put it in and reset serialno at BoS
|
||||
if (status) {
|
||||
OG(&go, stream_pagein, &v->state, &v->page);
|
||||
if (OG(&go, page_bos, &v->page)) OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page));
|
||||
}
|
||||
}
|
||||
|
||||
/* odd packets are not audio and should be discarded. With no packet, we
|
||||
* return a negative value when there is really nothing more to proceed */
|
||||
if (status > 0 && (v->packet.packet[0] & 0x01) == 0) packet = status;
|
||||
else if (stream.state > DISCONNECT || _buf_used(streambuf)) packet = 0;
|
||||
|
||||
UNLOCK_S;
|
||||
return packet;
|
||||
}
|
||||
|
||||
static int read_vorbis_header(void) {
|
||||
int done = 0;
|
||||
bool fetch = true;
|
||||
|
||||
LOCK_S;
|
||||
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
|
||||
|
||||
while (bytes && !done) {
|
||||
int status;
|
||||
|
||||
// get aligned to a page and ready to bring it in
|
||||
do {
|
||||
size_t consumed = min(bytes, 4096);
|
||||
|
||||
char* buffer = OG(&go, sync_buffer, &v->sync, consumed);
|
||||
memcpy(buffer, streambuf->readp, consumed);
|
||||
OG(&go, sync_wrote, &v->sync, consumed);
|
||||
|
||||
_buf_inc_readp(streambuf, consumed);
|
||||
bytes -= consumed;
|
||||
|
||||
status = fetch ? OG(&go, sync_pageout, &v->sync, &v->page) :
|
||||
OG(&go, sync_pageseek, &v->sync, &v->page);
|
||||
} while (bytes && status <= 0);
|
||||
|
||||
// nothing has been found and we have no more bytes, come back later
|
||||
if (status <= 0) break;
|
||||
|
||||
// always set stream serialno if we have a new one
|
||||
if (OG(&go, page_bos, &v->page)) OG(&go, stream_reset_serialno, &v->state, OG(&go, page_serialno, &v->page));
|
||||
|
||||
// bring new page in if we want it (otherwise we're just skipping)
|
||||
if (fetch) OG(&go, stream_pagein, &v->state, &v->page);
|
||||
|
||||
// not a switch...case b/c we might have multiple packets in a page in vorbis
|
||||
if (v->status == OGG_ID_HEADER) {
|
||||
// we need the id packet, get more pages if we don't
|
||||
if (!OG(&go, stream_packetout, &v->state, &v->packet)) continue;
|
||||
|
||||
OV(&gv, info_init, &v->info);
|
||||
status = OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet);
|
||||
|
||||
if (status) {
|
||||
LOG_ERROR("vorbis id header packet error %d", status);
|
||||
status = -1;
|
||||
LOG_ERROR("id header packet error %d", status);
|
||||
done = -1;
|
||||
} else {
|
||||
v->channels = v->info.channels;
|
||||
v->rate = v->info.rate;
|
||||
v->status = OGG_COMMENT_HEADER;
|
||||
|
||||
// only fetch if no other packet already in (they should not)
|
||||
fetch = OG(&go, page_packets, &v->page) <= 1;
|
||||
if (!fetch) LOG_INFO("id packet should terminate page");
|
||||
fetch = false;
|
||||
LOG_INFO("id acquired");
|
||||
// we should only have one packet, so get next pages
|
||||
if (OG(&go, page_packets, &v->page) == 1) continue;
|
||||
}
|
||||
break;
|
||||
case OGG_SETUP_HEADER:
|
||||
// header packets don't align with pages on Vorbis (contrary to Opus)
|
||||
if (fetch) OG(&go, stream_pagein, &v->state, &v->page);
|
||||
}
|
||||
|
||||
if (v->status == OGG_COMMENT_HEADER) {
|
||||
// don't consume VorbisComment which could be a huge packet, just skip it
|
||||
int packets = OG(&go, page_packets, &v->page);
|
||||
if (!packets) continue;
|
||||
|
||||
// we have a "fake" comment packet that is just has the last page...
|
||||
v->status = OGG_SETUP_HEADER;
|
||||
OG(&go, stream_pagein, &v->state, &v->page);
|
||||
OG(&go, stream_packetout, &v->state, &v->packet);
|
||||
|
||||
OV(&gv, comment_init, &v->comment);
|
||||
v->comment.vendor = "N/A";
|
||||
fetch = true;
|
||||
LOG_INFO("comment skipped successfully");
|
||||
|
||||
// because of lack of page alignment, we might have the setup page already fully in
|
||||
if (packets == 1) continue;
|
||||
}
|
||||
|
||||
if (v->status == OGG_SETUP_HEADER) {
|
||||
// we need the setup packet, get more pages if we don't
|
||||
if (OG(&go, stream_packetout, &v->state, &v->packet) <= 0) continue;
|
||||
|
||||
// finally build a codec if we have the packet
|
||||
status = OG(&go, stream_packetout, &v->state, &v->packet);
|
||||
if (status && ((status = OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet)) ||
|
||||
(status = OV(&gv, synthesis_init, &v->decoder, &v->info)))) {
|
||||
LOG_ERROR("vorbis setup header packet error %d", status);
|
||||
if (OV(&gv, synthesis_headerin, &v->info, &v->comment, &v->packet) ||
|
||||
OV(&gv, synthesis_init, &v->decoder, &v->info)) {
|
||||
LOG_ERROR("setup header packet error %d", status);
|
||||
// no need to free comment, it's fake
|
||||
OV(&gv, info_clear, &v->info);
|
||||
status = -1;
|
||||
done = -1;
|
||||
} else {
|
||||
OV(&gv, block_init, &v->decoder, &v->block);
|
||||
v->opened = true;
|
||||
LOG_INFO("codec up and running (rate: %d, channels:%d)", v->rate, v->channels);
|
||||
status = 1;
|
||||
LOG_INFO("codec up and running");
|
||||
done = 1;
|
||||
}
|
||||
//@FIXME: can we have audio on that page as well?
|
||||
break;
|
||||
case OGG_COMMENT_HEADER: {
|
||||
// don't consume VorbisComment, just skip it
|
||||
int packets = OG(&go, page_packets, &v->page);
|
||||
if (packets) {
|
||||
v->status = OGG_SETUP_HEADER;
|
||||
OG(&go, stream_pagein, &v->state, &v->page);
|
||||
OG(&go, stream_packetout, &v->state, &v->packet);
|
||||
|
||||
OV(&gv, comment_init, &v->comment);
|
||||
v->comment.vendor = "N/A";
|
||||
|
||||
// because of lack of page alignment, we might have the setup page already fully in
|
||||
if (packets > 1) fetch = false;
|
||||
LOG_INFO("comment skipped succesfully");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UNLOCK_S;
|
||||
return status;
|
||||
return done;
|
||||
}
|
||||
|
||||
inline int pcm_out(vorbis_dsp_state* decoder, void*** pcm) {
|
||||
@@ -310,12 +326,12 @@ static decode_state vorbis_decode(void) {
|
||||
if (v->overflow) {
|
||||
n = pcm_out(&v->decoder, &pcm);
|
||||
v->overflow = n - min(n, frames);
|
||||
} else if ((packet = get_ogg_packet()) > 0) {
|
||||
} else if ((packet = get_audio_packet()) > 0) {
|
||||
n = OV(&gv, synthesis, &v->block, &v->packet);
|
||||
if (n == 0) n = OV(&gv, synthesis_blockin, &v->decoder, &v->block);
|
||||
if (n == 0) n = pcm_out(&v->decoder, &pcm);
|
||||
v->overflow = n - min(n, frames);
|
||||
} else if (!packet && !OG(&go, page_eos, &v->page)) {
|
||||
} else if (!packet) {
|
||||
UNLOCK_O_direct;
|
||||
return DECODE_RUNNING;
|
||||
}
|
||||
@@ -396,33 +412,32 @@ static decode_state vorbis_decode(void) {
|
||||
}
|
||||
|
||||
static void vorbis_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
|
||||
LOG_INFO("OPENING CODEC");
|
||||
if (v->opened) {
|
||||
OV(&go, block_clear, &v->block);
|
||||
OV(&go, info_clear, &v->info);
|
||||
OV(&go, dsp_clear, &v->decoder);
|
||||
OV(&gv, block_clear, &v->block);
|
||||
OV(&gv, dsp_clear, &v->decoder);
|
||||
OV(&gv, info_clear, &v->info);
|
||||
}
|
||||
|
||||
v->opened = false;
|
||||
v->status = OGG_SYNC;
|
||||
v->status = OGG_ID_HEADER;
|
||||
v->overflow = 0;
|
||||
|
||||
OG(&gu, sync_clear, &v->sync);
|
||||
OG(&gu, stream_clear, &v->state);
|
||||
OG(&gu, stream_init, &v->state, -1);
|
||||
|
||||
OG(&go, stream_clear, &v->state);
|
||||
OG(&go, sync_clear, &v->sync);
|
||||
OG(&go, stream_init, &v->state, -1);
|
||||
}
|
||||
|
||||
static void vorbis_close() {
|
||||
return;
|
||||
LOG_INFO("CLOSING CODEC");
|
||||
if (v->opened) {
|
||||
OV(&go, block_clear, &v->block);
|
||||
OV(&go, info_clear, &v->info);
|
||||
OV(&go, dsp_clear, &v->decoder);
|
||||
OV(&gv, block_clear, &v->block);
|
||||
OV(&gv, dsp_clear, &v->decoder);
|
||||
// info must be last otherwise there is memory leak (where is it said... nowhere)
|
||||
OV(&gv, info_clear, &v->info);
|
||||
// we don' t have comments to free
|
||||
}
|
||||
|
||||
v->opened = false;
|
||||
|
||||
|
||||
OG(&go, stream_clear, &v->state);
|
||||
OG(&go, sync_clear, &v->sync);
|
||||
}
|
||||
@@ -469,6 +484,11 @@ static bool load_vorbis() {
|
||||
v_handle.ov_read = dlsym(handle, "ov_read");
|
||||
v_handle.ov_info = dlsym(handle, "ov_info");
|
||||
v_handle.ov_clear = dlsym(handle, "ov_clear");
|
||||
v.handle.ov_info_clear = dlsym(gv.handle, "vorbis_info_clear");
|
||||
v.handle.ov_comment_init = dlsym(gv.handle, "vorbis_comment_init");
|
||||
v.handle.ov_comment_clear = dlsym(gv.handle, "vorbis_comment_clear");
|
||||
v.handle.ov_block_init = dlsym(gv.handle, "vorbis_block_init");
|
||||
v.handle.ov_block_clear = dlsym(gv.handle, "vorbis_block_clear");
|
||||
v_handle.ov_open_callbacks = dlsym(handle, "ov_open_callbacks");
|
||||
|
||||
if ((err = dlerror()) != NULL) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
5
components/wifi-manager/webapp/dist/css/index.4bbe29a78a667faa2b6f.css
vendored
Normal file
5
components/wifi-manager/webapp/dist/css/index.4bbe29a78a667faa2b6f.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
components/wifi-manager/webapp/dist/css/index.4bbe29a78a667faa2b6f.css.gz
vendored
Normal file
BIN
components/wifi-manager/webapp/dist/css/index.4bbe29a78a667faa2b6f.css.gz
vendored
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
components/wifi-manager/webapp/dist/index.html.gz
vendored
BIN
components/wifi-manager/webapp/dist/index.html.gz
vendored
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
2
components/wifi-manager/webapp/dist/js/index.baf383.bundle.js
vendored
Normal file
2
components/wifi-manager/webapp/dist/js/index.baf383.bundle.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
components/wifi-manager/webapp/dist/js/index.baf383.bundle.js.gz
vendored
Normal file
BIN
components/wifi-manager/webapp/dist/js/index.baf383.bundle.js.gz
vendored
Normal file
Binary file not shown.
1
components/wifi-manager/webapp/dist/js/index.baf383.bundle.js.map
vendored
Normal file
1
components/wifi-manager/webapp/dist/js/index.baf383.bundle.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,2 +0,0 @@
|
||||
declare function _exports(grunt: any): void;
|
||||
export = _exports;
|
||||
@@ -1,2 +0,0 @@
|
||||
declare function _exports(grunt: any): void;
|
||||
export = _exports;
|
||||
@@ -1,2 +0,0 @@
|
||||
declare function _exports(grunt: any): void;
|
||||
export = _exports;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,688 +0,0 @@
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Alert extends BaseComponent {
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
close(): void;
|
||||
_destroyElement(): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Button extends BaseComponent {
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
toggle(): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Carousel extends BaseComponent {
|
||||
static get Default(): {
|
||||
interval: number;
|
||||
keyboard: boolean;
|
||||
pause: string;
|
||||
ride: boolean;
|
||||
touch: boolean;
|
||||
wrap: boolean;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
interval: string;
|
||||
keyboard: string;
|
||||
pause: string;
|
||||
ride: string;
|
||||
touch: string;
|
||||
wrap: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
_interval: NodeJS.Timeout;
|
||||
_activeElement: any;
|
||||
_isSliding: boolean;
|
||||
touchTimeout: NodeJS.Timeout;
|
||||
_swipeHelper: Swipe;
|
||||
_indicatorsElement: any;
|
||||
next(): void;
|
||||
nextWhenVisible(): void;
|
||||
prev(): void;
|
||||
pause(): void;
|
||||
cycle(): void;
|
||||
_maybeEnableCycle(): void;
|
||||
to(index: any): void;
|
||||
_addEventListeners(): void;
|
||||
_addTouchEventListeners(): void;
|
||||
_keydown(event: any): void;
|
||||
_getItemIndex(element: any): number;
|
||||
_setActiveIndicatorElement(index: any): void;
|
||||
_updateInterval(): void;
|
||||
_slide(order: any, element?: any): void;
|
||||
_isAnimated(): any;
|
||||
_getActive(): any;
|
||||
_getItems(): any[];
|
||||
_clearInterval(): void;
|
||||
_directionToOrder(direction: any): "next" | "prev";
|
||||
_orderToDirection(order: any): "right" | "left";
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Collapse extends BaseComponent {
|
||||
static get Default(): {
|
||||
parent: any;
|
||||
toggle: boolean;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
parent: string;
|
||||
toggle: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
_isTransitioning: boolean;
|
||||
_triggerArray: any[];
|
||||
toggle(): void;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
_isShown(element?: any): any;
|
||||
_getDimension(): "width" | "height";
|
||||
_initializeChildren(): void;
|
||||
_getFirstLevelChildren(selector: any): any[];
|
||||
_addAriaAndCollapsedClass(triggerArray: any, isOpen: any): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Dropdown extends BaseComponent {
|
||||
static get Default(): {
|
||||
autoClose: boolean;
|
||||
boundary: string;
|
||||
display: string;
|
||||
offset: number[];
|
||||
popperConfig: any;
|
||||
reference: string;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
autoClose: string;
|
||||
boundary: string;
|
||||
display: string;
|
||||
offset: string;
|
||||
popperConfig: string;
|
||||
reference: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
static clearMenus(event: any): void;
|
||||
static dataApiKeydownHandler(event: any): void;
|
||||
_popper: Popper.Instance;
|
||||
_parent: any;
|
||||
_menu: any;
|
||||
_inNavbar: boolean;
|
||||
toggle(): void;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
update(): void;
|
||||
_completeHide(relatedTarget: any): void;
|
||||
_createPopper(): void;
|
||||
_isShown(): any;
|
||||
_getPlacement(): "top" | "bottom" | "top-end" | "top-start" | "bottom-end" | "bottom-start" | "left-start" | "right-start";
|
||||
_detectNavbar(): boolean;
|
||||
_getOffset(): any;
|
||||
_getPopperConfig(): any;
|
||||
_selectMenuItem({ key, target }: {
|
||||
key: any;
|
||||
target: any;
|
||||
}): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Modal extends BaseComponent {
|
||||
static get Default(): {
|
||||
backdrop: boolean;
|
||||
focus: boolean;
|
||||
keyboard: boolean;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
backdrop: string;
|
||||
focus: string;
|
||||
keyboard: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any, relatedTarget: any): any;
|
||||
_dialog: any;
|
||||
_backdrop: Backdrop;
|
||||
_focustrap: FocusTrap;
|
||||
_isShown: boolean;
|
||||
_isTransitioning: boolean;
|
||||
_scrollBar: ScrollBarHelper;
|
||||
toggle(relatedTarget: any): void;
|
||||
show(relatedTarget: any): void;
|
||||
hide(): void;
|
||||
handleUpdate(): void;
|
||||
_initializeBackDrop(): Backdrop;
|
||||
_initializeFocusTrap(): FocusTrap;
|
||||
_showElement(relatedTarget: any): void;
|
||||
_addEventListeners(): void;
|
||||
_hideModal(): void;
|
||||
_isAnimated(): any;
|
||||
_triggerBackdropTransition(): void;
|
||||
/**
|
||||
* The following methods are used to handle overflowing modals
|
||||
*/
|
||||
_adjustDialog(): void;
|
||||
_resetAdjustments(): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Offcanvas extends BaseComponent {
|
||||
static get Default(): {
|
||||
backdrop: boolean;
|
||||
keyboard: boolean;
|
||||
scroll: boolean;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
backdrop: string;
|
||||
keyboard: string;
|
||||
scroll: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
_isShown: boolean;
|
||||
_backdrop: Backdrop;
|
||||
_focustrap: FocusTrap;
|
||||
toggle(relatedTarget: any): void;
|
||||
show(relatedTarget: any): void;
|
||||
hide(): void;
|
||||
_initializeBackDrop(): Backdrop;
|
||||
_initializeFocusTrap(): FocusTrap;
|
||||
_addEventListeners(): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Popover extends Tooltip {
|
||||
static get Default(): {
|
||||
content: string;
|
||||
offset: number[];
|
||||
placement: string;
|
||||
template: string;
|
||||
trigger: string;
|
||||
allowList: {
|
||||
'*': (string | RegExp)[];
|
||||
a: string[];
|
||||
area: any[];
|
||||
b: any[];
|
||||
br: any[];
|
||||
col: any[];
|
||||
code: any[];
|
||||
div: any[];
|
||||
em: any[];
|
||||
hr: any[];
|
||||
h1: any[];
|
||||
h2: any[];
|
||||
h3: any[];
|
||||
h4: any[];
|
||||
h5: any[];
|
||||
h6: any[];
|
||||
i: any[];
|
||||
img: string[];
|
||||
li: any[];
|
||||
ol: any[];
|
||||
p: any[];
|
||||
pre: any[];
|
||||
s: any[];
|
||||
small: any[];
|
||||
span: any[];
|
||||
sub: any[];
|
||||
sup: any[];
|
||||
strong: any[];
|
||||
u: any[];
|
||||
ul: any[];
|
||||
};
|
||||
animation: boolean;
|
||||
boundary: string;
|
||||
container: boolean;
|
||||
customClass: string;
|
||||
delay: number;
|
||||
fallbackPlacements: string[];
|
||||
html: boolean;
|
||||
popperConfig: any;
|
||||
sanitize: boolean;
|
||||
sanitizeFn: any;
|
||||
selector: boolean;
|
||||
title: string;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
content: string;
|
||||
allowList: string;
|
||||
animation: string;
|
||||
boundary: string;
|
||||
container: string;
|
||||
customClass: string;
|
||||
delay: string;
|
||||
fallbackPlacements: string;
|
||||
html: string;
|
||||
offset: string;
|
||||
placement: string;
|
||||
popperConfig: string;
|
||||
sanitize: string;
|
||||
sanitizeFn: string;
|
||||
selector: string;
|
||||
template: string;
|
||||
title: string;
|
||||
trigger: string;
|
||||
};
|
||||
_isWithContent(): any;
|
||||
_getContentForTemplate(): {
|
||||
".popover-header": any;
|
||||
".popover-body": any;
|
||||
};
|
||||
_getContent(): any;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class ScrollSpy extends BaseComponent {
|
||||
static get Default(): {
|
||||
offset: any;
|
||||
rootMargin: string;
|
||||
smoothScroll: boolean;
|
||||
target: any;
|
||||
threshold: number[];
|
||||
};
|
||||
static get DefaultType(): {
|
||||
offset: string;
|
||||
rootMargin: string;
|
||||
smoothScroll: string;
|
||||
target: string;
|
||||
threshold: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
_targetLinks: Map<any, any>;
|
||||
_observableSections: Map<any, any>;
|
||||
_rootElement: any;
|
||||
_activeTarget: any;
|
||||
_observer: IntersectionObserver;
|
||||
_previousScrollData: {
|
||||
visibleEntryTop: number;
|
||||
parentScrollTop: number;
|
||||
};
|
||||
refresh(): void;
|
||||
_maybeEnableSmoothScroll(): void;
|
||||
_getNewObserver(): IntersectionObserver;
|
||||
_observerCallback(entries: any): void;
|
||||
_initializeTargetsAndObservables(): void;
|
||||
_process(target: any): void;
|
||||
_activateParents(target: any): void;
|
||||
_clearActiveClass(parent: any): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Tab extends BaseComponent {
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
constructor(element: any);
|
||||
_parent: any;
|
||||
show(): void;
|
||||
_activate(element: any, relatedElem: any): void;
|
||||
_deactivate(element: any, relatedElem: any): void;
|
||||
_keydown(event: any): void;
|
||||
_getChildren(): any[];
|
||||
_getActiveElem(): any;
|
||||
_setInitialAttributes(parent: any, children: any): void;
|
||||
_setInitialAttributesOnChild(child: any): void;
|
||||
_setInitialAttributesOnTargetPanel(child: any): void;
|
||||
_toggleDropDown(element: any, open: any): void;
|
||||
_setAttributeIfNotExists(element: any, attribute: any, value: any): void;
|
||||
_elemIsActive(elem: any): any;
|
||||
_getInnerElement(elem: any): any;
|
||||
_getOuterElement(elem: any): any;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Toast extends BaseComponent {
|
||||
static get Default(): {
|
||||
animation: boolean;
|
||||
autohide: boolean;
|
||||
delay: number;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
animation: string;
|
||||
autohide: string;
|
||||
delay: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
_timeout: NodeJS.Timeout;
|
||||
_hasMouseInteraction: boolean;
|
||||
_hasKeyboardInteraction: boolean;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
isShown(): any;
|
||||
_maybeScheduleHide(): void;
|
||||
_onInteraction(event: any, isInteracting: any): void;
|
||||
_setListeners(): void;
|
||||
_clearTimeout(): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
export class Tooltip extends BaseComponent {
|
||||
static get Default(): {
|
||||
allowList: {
|
||||
'*': (string | RegExp)[];
|
||||
a: string[];
|
||||
area: any[];
|
||||
b: any[];
|
||||
br: any[];
|
||||
col: any[];
|
||||
code: any[];
|
||||
div: any[];
|
||||
em: any[];
|
||||
hr: any[];
|
||||
h1: any[];
|
||||
h2: any[];
|
||||
h3: any[];
|
||||
h4: any[];
|
||||
h5: any[];
|
||||
h6: any[];
|
||||
i: any[];
|
||||
img: string[];
|
||||
li: any[];
|
||||
ol: any[];
|
||||
p: any[];
|
||||
pre: any[];
|
||||
s: any[];
|
||||
small: any[];
|
||||
span: any[];
|
||||
sub: any[];
|
||||
sup: any[];
|
||||
strong: any[];
|
||||
u: any[];
|
||||
ul: any[];
|
||||
};
|
||||
animation: boolean;
|
||||
boundary: string;
|
||||
container: boolean;
|
||||
customClass: string;
|
||||
delay: number;
|
||||
fallbackPlacements: string[];
|
||||
html: boolean;
|
||||
offset: number[];
|
||||
placement: string;
|
||||
popperConfig: any;
|
||||
sanitize: boolean;
|
||||
sanitizeFn: any;
|
||||
selector: boolean;
|
||||
template: string;
|
||||
title: string;
|
||||
trigger: string;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
allowList: string;
|
||||
animation: string;
|
||||
boundary: string;
|
||||
container: string;
|
||||
customClass: string;
|
||||
delay: string;
|
||||
fallbackPlacements: string;
|
||||
html: string;
|
||||
offset: string;
|
||||
placement: string;
|
||||
popperConfig: string;
|
||||
sanitize: string;
|
||||
sanitizeFn: string;
|
||||
selector: string;
|
||||
template: string;
|
||||
title: string;
|
||||
trigger: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
static jQueryInterface(config: any): any;
|
||||
_isEnabled: boolean;
|
||||
_timeout: number;
|
||||
_isHovered: boolean;
|
||||
_activeTrigger: {};
|
||||
_popper: Popper.Instance;
|
||||
_templateFactory: TemplateFactory;
|
||||
_newContent: any;
|
||||
tip: Element;
|
||||
enable(): void;
|
||||
disable(): void;
|
||||
toggleEnabled(): void;
|
||||
toggle(): void;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
update(): void;
|
||||
_isWithContent(): boolean;
|
||||
_getTipElement(): Element;
|
||||
_createTipElement(content: any): Element;
|
||||
setContent(content: any): void;
|
||||
_getTemplateFactory(content: any): TemplateFactory;
|
||||
_getContentForTemplate(): {
|
||||
".tooltip-inner": any;
|
||||
};
|
||||
_getTitle(): any;
|
||||
_initializeOnDelegatedTarget(event: any): any;
|
||||
_isAnimated(): any;
|
||||
_isShown(): boolean;
|
||||
_createPopper(tip: any): Popper.Instance;
|
||||
_getOffset(): any;
|
||||
_resolvePossibleFunction(arg: any): any;
|
||||
_getPopperConfig(attachment: any): any;
|
||||
_setListeners(): void;
|
||||
_hideModalHandler: () => void;
|
||||
_fixTitle(): void;
|
||||
_enter(): void;
|
||||
_leave(): void;
|
||||
_setTimeout(handler: any, timeout: any): void;
|
||||
_isWithActiveTrigger(): boolean;
|
||||
_getDelegateConfig(): {
|
||||
selector: boolean;
|
||||
trigger: string;
|
||||
};
|
||||
_disposePopper(): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
declare class BaseComponent extends Config {
|
||||
static getInstance(element: any): any;
|
||||
static getOrCreateInstance(element: any, config?: {}): any;
|
||||
static get VERSION(): string;
|
||||
static get DATA_KEY(): string;
|
||||
static get EVENT_KEY(): string;
|
||||
static eventName(name: any): string;
|
||||
constructor(element: any, config: any);
|
||||
_element: any;
|
||||
_config: any;
|
||||
dispose(): void;
|
||||
_queueCallback(callback: any, element: any, isAnimated?: boolean): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
declare class Swipe extends Config {
|
||||
static get Default(): {
|
||||
endCallback: any;
|
||||
leftCallback: any;
|
||||
rightCallback: any;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
endCallback: string;
|
||||
leftCallback: string;
|
||||
rightCallback: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
static isSupported(): boolean;
|
||||
constructor(element: any, config: any);
|
||||
_element: any;
|
||||
_config: any;
|
||||
_deltaX: number;
|
||||
_supportPointerEvents: boolean;
|
||||
dispose(): void;
|
||||
_start(event: any): void;
|
||||
_end(event: any): void;
|
||||
_move(event: any): void;
|
||||
_handleSwipe(): void;
|
||||
_initEvents(): void;
|
||||
_eventIsPointerPenTouch(event: any): boolean;
|
||||
}
|
||||
import * as Popper from "@popperjs/core";
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
declare class Backdrop extends Config {
|
||||
static get Default(): {
|
||||
className: string;
|
||||
clickCallback: any;
|
||||
isAnimated: boolean;
|
||||
isVisible: boolean;
|
||||
rootElement: string;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
className: string;
|
||||
clickCallback: string;
|
||||
isAnimated: string;
|
||||
isVisible: string;
|
||||
rootElement: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
constructor(config: any);
|
||||
_config: any;
|
||||
_isAppended: boolean;
|
||||
_element: HTMLDivElement;
|
||||
show(callback: any): void;
|
||||
hide(callback: any): void;
|
||||
dispose(): void;
|
||||
_getElement(): HTMLDivElement;
|
||||
_append(): void;
|
||||
_emulateAnimation(callback: any): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
declare class FocusTrap extends Config {
|
||||
static get Default(): {
|
||||
autofocus: boolean;
|
||||
trapElement: any;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
autofocus: string;
|
||||
trapElement: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
constructor(config: any);
|
||||
_config: any;
|
||||
_isActive: boolean;
|
||||
_lastTabNavDirection: string;
|
||||
activate(): void;
|
||||
deactivate(): void;
|
||||
_handleFocusin(event: any): void;
|
||||
_handleKeydown(event: any): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
declare class ScrollBarHelper {
|
||||
_element: HTMLElement;
|
||||
getWidth(): number;
|
||||
hide(): void;
|
||||
reset(): void;
|
||||
isOverflowing(): boolean;
|
||||
_disableOverFlow(): void;
|
||||
_setElementAttributes(selector: any, styleProperty: any, callback: any): void;
|
||||
_saveInitialAttribute(element: any, styleProperty: any): void;
|
||||
_resetElementAttributes(selector: any, styleProperty: any): void;
|
||||
_applyManipulationCallback(selector: any, callBack: any): void;
|
||||
}
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
declare class TemplateFactory extends Config {
|
||||
static get Default(): {
|
||||
allowList: {
|
||||
'*': (string | RegExp)[];
|
||||
a: string[];
|
||||
area: any[];
|
||||
b: any[];
|
||||
br: any[];
|
||||
col: any[];
|
||||
code: any[];
|
||||
div: any[];
|
||||
em: any[];
|
||||
hr: any[];
|
||||
h1: any[];
|
||||
h2: any[];
|
||||
h3: any[];
|
||||
h4: any[];
|
||||
h5: any[];
|
||||
h6: any[];
|
||||
i: any[];
|
||||
img: string[];
|
||||
li: any[];
|
||||
ol: any[];
|
||||
p: any[];
|
||||
pre: any[];
|
||||
s: any[];
|
||||
small: any[];
|
||||
span: any[];
|
||||
sub: any[];
|
||||
sup: any[];
|
||||
strong: any[];
|
||||
u: any[];
|
||||
ul: any[];
|
||||
};
|
||||
content: {};
|
||||
extraClass: string;
|
||||
html: boolean;
|
||||
sanitize: boolean;
|
||||
sanitizeFn: any;
|
||||
template: string;
|
||||
};
|
||||
static get DefaultType(): {
|
||||
allowList: string;
|
||||
content: string;
|
||||
extraClass: string;
|
||||
html: string;
|
||||
sanitize: string;
|
||||
sanitizeFn: string;
|
||||
template: string;
|
||||
};
|
||||
static get NAME(): string;
|
||||
constructor(config: any);
|
||||
_config: any;
|
||||
getContent(): any[];
|
||||
hasContent(): boolean;
|
||||
changeContent(content: any): TemplateFactory;
|
||||
toHtml(): Element;
|
||||
_typeCheckConfig(config: any): void;
|
||||
_checkContent(arg: any): void;
|
||||
_setContent(template: any, content: any, selector: any): void;
|
||||
_maybeSanitize(arg: any): any;
|
||||
_resolvePossibleFunction(arg: any): any;
|
||||
_putElementInTemplate(element: any, templateElement: any): void;
|
||||
}
|
||||
/**
|
||||
* --------------------------------------------------------------------------
|
||||
* Bootstrap util/config.js
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
/**
|
||||
* Class definition
|
||||
*/
|
||||
declare class Config {
|
||||
static get Default(): {};
|
||||
static get DefaultType(): {};
|
||||
static get NAME(): void;
|
||||
_getConfig(config: any): any;
|
||||
_configAfterMerge(config: any): any;
|
||||
_mergeConfigObj(config: any, element: any): any;
|
||||
_typeCheckConfig(config: any, configTypes?: any): void;
|
||||
}
|
||||
export {};
|
||||
@@ -1,93 +0,0 @@
|
||||
/// <reference lib="webworker" />
|
||||
export namespace languages {
|
||||
export namespace markup {
|
||||
namespace tag { }
|
||||
}
|
||||
import html = markup;
|
||||
export { html };
|
||||
import mathml = markup;
|
||||
export { mathml };
|
||||
import svg = markup;
|
||||
export { svg };
|
||||
export const xml: {
|
||||
[x: string]: RegExp | GrammarToken | (RegExp | GrammarToken)[];
|
||||
};
|
||||
import ssml = xml;
|
||||
export { ssml };
|
||||
import atom = xml;
|
||||
export { atom };
|
||||
import rss = xml;
|
||||
export { rss };
|
||||
export const clike: {
|
||||
comment: {
|
||||
pattern: RegExp;
|
||||
lookbehind: boolean;
|
||||
greedy: boolean;
|
||||
}[];
|
||||
string: {
|
||||
pattern: RegExp;
|
||||
greedy: boolean;
|
||||
};
|
||||
'class-name': {
|
||||
pattern: RegExp;
|
||||
lookbehind: boolean;
|
||||
inside: {
|
||||
punctuation: RegExp;
|
||||
};
|
||||
};
|
||||
keyword: RegExp;
|
||||
boolean: RegExp;
|
||||
function: RegExp;
|
||||
number: RegExp;
|
||||
operator: RegExp;
|
||||
punctuation: RegExp;
|
||||
};
|
||||
export const javascript: {
|
||||
[x: string]: RegExp | GrammarToken | (RegExp | GrammarToken)[];
|
||||
};
|
||||
import js = javascript;
|
||||
export { js };
|
||||
}
|
||||
/**
|
||||
* The expansion of a simple `RegExp` literal to support additional properties.
|
||||
*/
|
||||
export type GrammarToken = {
|
||||
/**
|
||||
* The regular expression of the token.
|
||||
*/
|
||||
pattern: RegExp;
|
||||
/**
|
||||
* If `true`, then the first capturing group of `pattern` will (effectively)
|
||||
* behave as a lookbehind group meaning that the captured text will not be part of the matched text of the new token.
|
||||
*/
|
||||
lookbehind?: boolean;
|
||||
/**
|
||||
* Whether the token is greedy.
|
||||
*/
|
||||
greedy?: boolean;
|
||||
/**
|
||||
* An optional alias or list of aliases.
|
||||
*/
|
||||
alias?: string | string[];
|
||||
/**
|
||||
* The nested grammar of this token.
|
||||
*
|
||||
* The `inside` grammar will be used to tokenize the text value of each token of this kind.
|
||||
*
|
||||
* This can be used to make nested and even recursive language definitions.
|
||||
*
|
||||
* Note: This can cause infinite recursion. Be careful when you embed different languages or even the same language into
|
||||
* each another.
|
||||
*/
|
||||
inside?: Grammar;
|
||||
};
|
||||
export type Grammar = {
|
||||
[x: string]: RegExp | GrammarToken | Array<RegExp | GrammarToken>;
|
||||
};
|
||||
/**
|
||||
* A function which will invoked after an element was successfully highlighted.
|
||||
*/
|
||||
export type HighlightCallback = (element: Element) => void;
|
||||
export type HookCallback = (env: {
|
||||
[x: string]: any;
|
||||
}) => void;
|
||||
@@ -64,6 +64,28 @@ declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getStatus(): {};
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
declare function getRadioButton(entry: any): string;
|
||||
@@ -196,6 +218,17 @@ declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare function pushStatus(): void;
|
||||
declare let sd: {};
|
||||
declare let rf: boolean;
|
||||
declare function refreshStatus(): void;
|
||||
|
||||
8
components/wifi-manager/webapp/dist/webpack/BuildEventsHook.d.ts
vendored
Normal file
8
components/wifi-manager/webapp/dist/webpack/BuildEventsHook.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export class BuildEventsHook {
|
||||
constructor(name: any, fn: any, stage?: string);
|
||||
name: any;
|
||||
stage: string;
|
||||
function: any;
|
||||
apply(compiler: any): void;
|
||||
}
|
||||
export function createBuildEventsHook(options: any): BuildEventsHook;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user