Compare commits

...

22 Commits

Author SHA1 Message Date
github-actions
84d22cce07 Update prebuilt objects [skip actions] 2023-04-07 19:17:01 +00:00
philippe44
0495b7ea7a release 2023-04-07 12:13:43 -07:00
Sebastien L
b98ddd76cb Merge branch 'master-v4.3' of https://github.com/sle118/squeezelite-esp32 into master-v4.3 2023-04-07 10:53:29 -04:00
Sebastien L
7ac628a29d Improved audio ui, bug fixes 2023-04-07 10:53:22 -04:00
philippe44
d2b8edce60 fix vorbis as well 2023-04-06 22:45:40 -07:00
philippe44
b4af1e7bef commit BT buttons 2023-04-05 16:33:39 -07:00
philippe44
f91392e044 preset buttons is 0..9 2023-04-05 15:29:48 -07:00
philippe44
1e0fce53c7 and decoder ... 2023-04-04 22:30:38 -07:00
philippe44
3372aaea69 NULL the overflow buffer 2023-04-04 22:21:33 -07:00
philippe44
8d1888a198 new opus decoder 2023-04-04 22:13:31 -07:00
philippe44
18b830eaa3 add preset 7..10 2023-04-02 13:40:07 -07:00
philippe44
cbc1ab38fb gain some .rodata from DMAP decoder (unused strings) 2023-04-01 21:03:10 -07:00
philippe44
27a0d2a4d3 fix Spotify volume normalization 2023-04-01 14:24:56 -07:00
philippe44
02fc039bbe maximize number of AirPlay RTP buffers 2023-04-01 12:37:52 -07:00
philippe44
6fad1f8251 fix empty seek table for good 2023-04-01 00:39:19 -07:00
philippe44
42621561df codec optimizations 2023-03-30 22:22:48 -07:00
philippe44
5ecb371fb0 pretty things up a bit 2023-03-29 23:41:35 -07:00
philippe44
cad286c8d7 provide squeezelite error log in UI 2023-03-29 23:02:16 -07:00
philippe44
a9a9018794 fix country code in cspot 2023-03-29 12:06:20 -07:00
github-actions
236cfef05d Update prebuilt objects [skip actions] 2023-03-29 07:18:33 +00:00
philippe44
02b61e0ab6 release 2023-03-29 00:14:42 -07:00
github-actions
226c483b0b Update prebuilt objects [skip actions] 2023-03-29 06:37:55 +00:00
87 changed files with 2140 additions and 922 deletions

View File

@@ -5,8 +5,9 @@
Squeezelite-esp32 is an audio software suite made to run on espressif's ESP32 wifi (b/g/n) and bluetooth chipset. It offers the following capabilities
- Stream your local music and connect to all major on-line music providers (Spotify, Deezer, Tidal, Qobuz) using [Logitech Media Server - a.k.a LMS](https://forums.slimdevices.com/) and enjoy multi-room audio synchronization. LMS can be extended by numerous plugins and can be controlled using a Web browser or dedicated applications (iPhone, Android). It can also send audio to UPnP, Sonos, ChromeCast and AirPlay speakers/devices.
- Stream from a Bluetooth device (iPhone, Android)
- Stream from an AirPlay controller (iPhone, iTunes ...) and enjoy synchronization multiroom as well (although it's AirPlay 1 only)
- Stream directly from a Bluetooth device (iPhone, Android)
- Stream directly from an AirPlay controller (iPhone, iTunes ...) and enjoy synchronization multiroom as well (although it's AirPlay 1 only)
- Stream direcly from Spotify using SpotifyConnect (thanks to [cspot](https://github.com/feelfreelinux/cspot)
Depending on the hardware connected to the ESP32, you can send audio to a local DAC, to SPDIF or to a Bluetooth speaker. The bare minimum required hardware is a WROVER module with 4MB of Flash and 4MB of PSRAM (https://www.espressif.com/en/products/modules/esp32). With that module standalone, just apply power and you can stream to a Bluetooth speaker. You can also send audio to most I2S DAC as well as to SPDIF receivers using just a cable or an optical transducer.

View File

@@ -1,5 +1,5 @@
idf_component_register(
INCLUDE_DIRS . ./inc inc/alac inc/helix-aac inc/mad inc/resample16 inc/soxr inc/vorbis inc/opus inc/opusfile
INCLUDE_DIRS . ./inc inc/alac inc/helix-aac inc/mad inc/resample16 inc/soxr inc/vorbis inc/opus
)
if (DEFINED AAC_DISABLE_SBR)
@@ -14,7 +14,6 @@ add_prebuilt_library(libvorbisidec lib/libvorbisidec.a )
add_prebuilt_library(libogg lib/libogg.a )
add_prebuilt_library(libalac lib/libalac.a )
add_prebuilt_library(libresample16 lib/libresample16.a )
add_prebuilt_library(libopusfile lib/libopusfile.a )
add_prebuilt_library(libopus lib/libopus.a )
target_link_libraries(${COMPONENT_LIB} INTERFACE libmad)
@@ -24,5 +23,4 @@ target_link_libraries(${COMPONENT_LIB} INTERFACE libvorbisidec)
target_link_libraries(${COMPONENT_LIB} INTERFACE libogg)
target_link_libraries(${COMPONENT_LIB} INTERFACE libalac)
target_link_libraries(${COMPONENT_LIB} INTERFACE libresample16)
target_link_libraries(${COMPONENT_LIB} INTERFACE libopusfile)
target_link_libraries(${COMPONENT_LIB} INTERFACE libopus)

View File

@@ -1,7 +1,7 @@
#ifndef __CONFIG_TYPES_H__
#define __CONFIG_TYPES_H__
/* these are filled in by configure */
/* these are filled in by configure or cmake*/
#define INCLUDE_INTTYPES_H 1
#define INCLUDE_STDINT_H 1
#define INCLUDE_SYS_TYPES_H 1
@@ -21,5 +21,6 @@ typedef uint16_t ogg_uint16_t;
typedef int32_t ogg_int32_t;
typedef uint32_t ogg_uint32_t;
typedef int64_t ogg_int64_t;
typedef uint64_t ogg_uint64_t;
#endif

View File

@@ -11,7 +11,6 @@
********************************************************************
function: toplevel libogg include
last mod: $Id$
********************************************************************/
#ifndef _OGG_H

View File

@@ -10,8 +10,7 @@
* *
********************************************************************
function: #ifdef jail to whip a few platforms into the UNIX ideal.
last mod: $Id$
function: Define a consistent set of types on each platform.
********************************************************************/
#ifndef _OS_TYPES_H
@@ -44,6 +43,7 @@
typedef unsigned long long ogg_uint64_t;
# elif defined(__MWERKS__)
typedef long long ogg_int64_t;
typedef unsigned long long ogg_uint64_t;
typedef int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef short ogg_int16_t;
@@ -62,6 +62,7 @@
typedef __int64 ogg_int64_t;
typedef __int32 ogg_int32_t;
typedef unsigned __int32 ogg_uint32_t;
typedef unsigned __int64 ogg_uint64_t;
typedef __int16 ogg_int16_t;
typedef unsigned __int16 ogg_uint16_t;
# endif
@@ -69,12 +70,13 @@
#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
# include <inttypes.h>
# include <sys/types.h>
typedef int16_t ogg_int16_t;
typedef uint16_t ogg_uint16_t;
typedef u_int16_t ogg_uint16_t;
typedef int32_t ogg_int32_t;
typedef uint32_t ogg_uint32_t;
typedef u_int32_t ogg_uint32_t;
typedef int64_t ogg_int64_t;
typedef u_int64_t ogg_uint64_t;
#elif defined(__HAIKU__)
@@ -85,6 +87,7 @@
typedef int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long ogg_int64_t;
typedef unsigned long long ogg_uint64_t;
#elif defined(__BEOS__)
@@ -95,6 +98,7 @@
typedef int32_t ogg_int32_t;
typedef uint32_t ogg_uint32_t;
typedef int64_t ogg_int64_t;
typedef uint64_t ogg_uint64_t;
#elif defined (__EMX__)
@@ -104,6 +108,8 @@
typedef int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long ogg_int64_t;
typedef unsigned long long ogg_uint64_t;
#elif defined (DJGPP)
@@ -112,11 +118,13 @@
typedef int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long ogg_int64_t;
typedef unsigned long long ogg_uint64_t;
#elif defined(R5900)
/* PS2 EE */
typedef long ogg_int64_t;
typedef unsigned long ogg_uint64_t;
typedef int ogg_int32_t;
typedef unsigned ogg_uint32_t;
typedef short ogg_int16_t;
@@ -129,6 +137,7 @@
typedef signed int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long int ogg_int64_t;
typedef unsigned long long int ogg_uint64_t;
#elif defined(__TMS320C6X__)
@@ -138,6 +147,7 @@
typedef signed int ogg_int32_t;
typedef unsigned int ogg_uint32_t;
typedef long long int ogg_int64_t;
typedef unsigned long long int ogg_uint64_t;
#else

View File

@@ -6,7 +6,7 @@
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE libopusfile SOURCE CODE IS (C) COPYRIGHT 1994-2012 *
* by the Xiph.Org Foundation and contributors http://www.xiph.org/ *
* by the Xiph.Org Foundation and contributors https://xiph.org/ *
* *
********************************************************************
@@ -28,7 +28,7 @@
reference
<tt><a href="https://www.xiph.org/ogg/doc/libogg/reference.html">libogg</a></tt>
and
<tt><a href="https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/index.html">libopus</a></tt>
<tt><a href="https://opus-codec.org/docs/opus_api-1.3.1/">libopus</a></tt>
libraries.
<tt>libopusfile</tt> provides several sets of built-in routines for
@@ -58,7 +58,7 @@
it is stored in the header to allow you to resample to it after decoding
(the <tt>libopusfile</tt> API does not currently provide a resampler,
but the
<a href="http://www.speex.org/docs/manual/speex-manual/node7.html#SECTION00760000000000000000">the
<a href="https://www.speex.org/docs/manual/speex-manual/node7.html#SECTION00760000000000000000">the
Speex resampler</a> is a good choice if you need one).
In general, if you are playing back the audio, you should leave it at
48&nbsp;kHz, provided your audio hardware supports it.
@@ -68,7 +68,7 @@
Opus files can contain anywhere from 1 to 255 channels of audio.
The channel mappings for up to 8 channels are the same as the
<a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis
<a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810004.3.9">Vorbis
mappings</a>.
A special stereo API can convert everything to 2 channels, making it simple
to support multichannel files in an application which only has stereo
@@ -147,18 +147,18 @@ typedef struct OggOpusFile OggOpusFile;
/**@endcond*/
/**\defgroup error_codes Error Codes*/
/*@{*/
/**@{*/
/**\name List of possible error codes
Many of the functions in this library return a negative error code when a
function fails.
This list provides a brief explanation of the common errors.
See each individual function for more details on what a specific error code
means in that context.*/
/*@{*/
/**@{*/
/**A request did not succeed.*/
#define OP_FALSE (-1)
/*Currently not used externally.*/
/**Currently not used externally.**/
#define OP_EOF (-2)
/**There was a hole in the page sequence numbers (e.g., a page was corrupt or
missing).*/
@@ -185,7 +185,7 @@ typedef struct OggOpusFile OggOpusFile;
#define OP_EBADHEADER (-133)
/**The ID header contained an unrecognized version number.*/
#define OP_EVERSION (-134)
/*Currently not used at all.*/
/**Currently not used at all.**/
#define OP_ENOTAUDIO (-135)
/**An audio packet failed to decode properly.
This is usually caused by a multistream Ogg packet where the durations of
@@ -200,11 +200,11 @@ typedef struct OggOpusFile OggOpusFile;
/**The first or last granule position of a link failed basic validity checks.*/
#define OP_EBADTIMESTAMP (-139)
/*@}*/
/*@}*/
/**@}*/
/**@}*/
/**\defgroup header_info Header Information*/
/*@{*/
/**@{*/
/**The maximum number of channels in an Ogg Opus stream.*/
#define OPUS_CHANNEL_COUNT_MAX (255)
@@ -284,7 +284,7 @@ struct OpusHead{
A particular tag may occur more than once, and order is significant.
The character set encoding for the strings is always UTF-8, but the tag
names are limited to ASCII, and treated as case-insensitive.
See <a href="http://www.xiph.org/vorbis/doc/v-comment.html">the Vorbis
See <a href="https://www.xiph.org/vorbis/doc/v-comment.html">the Vorbis
comment header specification</a> for details.
In filling in this structure, <tt>libopusfile</tt> will null-terminate the
@@ -311,7 +311,7 @@ struct OpusTags{
};
/**\name Picture tag image formats*/
/*@{*/
/**@{*/
/**The MIME type was not recognized, or the image data did not match the
declared MIME type.*/
@@ -325,7 +325,7 @@ struct OpusTags{
/**The image is a GIF.*/
#define OP_PIC_FORMAT_GIF (3)
/*@}*/
/**@}*/
/**The contents of a METADATA_BLOCK_PICTURE tag.*/
struct OpusPictureTag{
@@ -398,7 +398,7 @@ struct OpusPictureTag{
These can be used to query the headers returned by <tt>libopusfile</tt>, or
to parse Opus headers from sources other than an Ogg Opus stream, provided
they use the same format.*/
/*@{*/
/**@{*/
/**Parses the contents of the ID header packet of an Ogg Opus stream.
\param[out] _head Returns the contents of the parsed packet.
@@ -671,12 +671,12 @@ void opus_picture_tag_init(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
\param _pic The #OpusPictureTag structure to clear.*/
void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
/*@}*/
/**@}*/
/*@}*/
/**@}*/
/**\defgroup url_options URL Reading Options*/
/*@{*/
/**@{*/
/**\name URL reading options
Options for op_url_stream_create() and associated functions.
These allow you to provide proxy configuration parameters, skip SSL
@@ -685,7 +685,7 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
times, only the value specified by the last occurrence has an effect
(unless otherwise specified).
They may be expanded in the future.*/
/*@{*/
/**@{*/
/**@cond PRIVATE*/
@@ -698,7 +698,7 @@ void opus_picture_tag_clear(OpusPictureTag *_pic) OP_ARG_NONNULL(1);
#define OP_HTTP_PROXY_PASS_REQUEST (6720)
#define OP_GET_SERVER_INFO_REQUEST (6784)
#define OP_URL_OPT(_request) ((_request)+(char *)0)
#define OP_URL_OPT(_request) ((char *)(_request))
/*These macros trigger compilation errors or warnings if the wrong types are
provided to one of the URL options.*/
@@ -843,11 +843,11 @@ void opus_server_info_clear(OpusServerInfo *_info) OP_ARG_NONNULL(1);
#define OP_GET_SERVER_INFO(_info) \
OP_URL_OPT(OP_GET_SERVER_INFO_REQUEST),OP_CHECK_SERVER_INFO_PTR(_info)
/*@}*/
/*@}*/
/**@}*/
/**@}*/
/**\defgroup stream_callbacks Abstract Stream Reading Interface*/
/*@{*/
/**@{*/
/**\name Functions for reading from streams
These functions define the interface used to read from and seek in a stream
of data.
@@ -856,7 +856,7 @@ void opus_server_info_clear(OpusServerInfo *_info) OP_ARG_NONNULL(1);
These functions also include some convenience routines for working with
standard <code>FILE</code> pointers, complete streams stored in a single
block of memory, or URLs.*/
/*@{*/
/**@{*/
/**Reads up to \a _nbytes bytes of data from \a _stream.
\param _stream The stream to read from.
@@ -1034,18 +1034,18 @@ OP_WARN_UNUSED_RESULT void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
OP_WARN_UNUSED_RESULT void *op_url_stream_create(OpusFileCallbacks *_cb,
const char *_url,...) OP_ARG_NONNULL(1) OP_ARG_NONNULL(2);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
/**\defgroup stream_open_close Opening and Closing*/
/*@{*/
/**@{*/
/**\name Functions for opening and closing streams
These functions allow you to test a stream to see if it is Opus, open it,
and close it.
Several flavors are provided for each of the built-in stream types, plus a
more general version which takes a set of application-provided callbacks.*/
/*@{*/
/**@{*/
/**Test to see if this is an Opus stream.
For good results, you will need at least 57 bytes (for a pure Opus-only
@@ -1159,20 +1159,16 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_url(const char *_url,
This value will be passed verbatim as the first
argument to all of the callbacks.
\param _cb The callbacks with which to access the stream.
<code><a href="#op_read_func">read()</a></code> must
be implemented.
<code><a href="#op_seek_func">seek()</a></code> and
<code><a href="#op_tell_func">tell()</a></code> may
be <code>NULL</code>, or may always return -1 to
indicate a stream is unseekable, but if
<code><a href="#op_seek_func">seek()</a></code> is
implemented and succeeds on a particular stream, then
<code><a href="#op_tell_func">tell()</a></code> must
also.
<code><a href="#op_close_func">close()</a></code> may
be <code>NULL</code>, but if it is not, it will be
called when the \c OggOpusFile is destroyed by
op_free().
\ref op_read_func "read()" must be implemented.
\ref op_seek_func "seek()" and \ref op_tell_func
"tell()" may be <code>NULL</code>, or may always
return -1 to indicate a stream is unseekable, but if
\ref op_seek_func "seek()" is implemented and
succeeds on a particular stream, then \ref
op_tell_func "tell()" must also.
\ref op_close_func "close()" may be <code>NULL</code>,
but if it is not, it will be called when the \c
OggOpusFile is destroyed by op_free().
It will not be called if op_open_callbacks() fails
with an error.
\param _initial_data An initial buffer of data from the start of the
@@ -1183,10 +1179,8 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_url(const char *_url,
stream to be opened, even if it is unseekable.
\param _initial_bytes The number of bytes in \a _initial_data.
If the stream is seekable, its current position (as
reported by
<code><a href="#opus_tell_func">tell()</a></code>
at the start of this function) must be equal to
\a _initial_bytes.
reported by \ref op_tell_func "tell()" at the start
of this function) must be equal to \a _initial_bytes.
Otherwise, seeking to absolute positions will
generate inconsistent results.
\param[out] _error Returns 0 on success, or a failure code on error.
@@ -1206,11 +1200,10 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_open_url(const char *_url,
implemented, such as an unsupported channel
family.</dd>
<dt>#OP_EINVAL</dt>
<dd><code><a href="#op_seek_func">seek()</a></code>
was implemented and succeeded on this source, but
<code><a href="#op_tell_func">tell()</a></code>
did not, or the starting position indicator was
not equal to \a _initial_bytes.</dd>
<dd>\ref op_seek_func "seek()" was implemented and
succeeded on this source, but \ref op_tell_func
"tell()" did not, or the starting position
indicator was not equal to \a _initial_bytes.</dd>
<dt>#OP_ENOTFORMAT</dt>
<dd>The stream contained a link that did not have
any logical Opus streams in it.</dd>
@@ -1341,20 +1334,16 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_url(const char *_url,
This value will be passed verbatim as the first
argument to all of the callbacks.
\param _cb The callbacks with which to access the stream.
<code><a href="#op_read_func">read()</a></code> must
be implemented.
<code><a href="#op_seek_func">seek()</a></code> and
<code><a href="#op_tell_func">tell()</a></code> may
be <code>NULL</code>, or may always return -1 to
indicate a stream is unseekable, but if
<code><a href="#op_seek_func">seek()</a></code> is
implemented and succeeds on a particular stream, then
<code><a href="#op_tell_func">tell()</a></code> must
also.
<code><a href="#op_close_func">close()</a></code> may
be <code>NULL</code>, but if it is not, it will be
called when the \c OggOpusFile is destroyed by
op_free().
\ref op_read_func "read()" must be implemented.
\ref op_seek_func "seek()" and \ref op_tell_func
"tell()" may be <code>NULL</code>, or may always
return -1 to indicate a stream is unseekable, but if
\ref op_seek_func "seek()" is implemented and
succeeds on a particular stream, then \ref
op_tell_func "tell()" must also.
\ref op_close_func "close()" may be <code>NULL</code>,
but if it is not, it will be called when the \c
OggOpusFile is destroyed by op_free().
It will not be called if op_open_callbacks() fails
with an error.
\param _initial_data An initial buffer of data from the start of the
@@ -1367,9 +1356,8 @@ OP_WARN_UNUSED_RESULT OggOpusFile *op_test_url(const char *_url,
\param _initial_bytes The number of bytes in \a _initial_data.
If the stream is seekable, its current position (as
reported by
<code><a href="#opus_tell_func">tell()</a></code>
at the start of this function) must be equal to
\a _initial_bytes.
\ref op_tell_func "tell()" at the start of this
function) must be equal to \a _initial_bytes.
Otherwise, seeking to absolute positions will
generate inconsistent results.
\param[out] _error Returns 0 on success, or a failure code on error.
@@ -1418,11 +1406,11 @@ int op_test_open(OggOpusFile *_of) OP_ARG_NONNULL(1);
\param _of The \c OggOpusFile to free.*/
void op_free(OggOpusFile *_of);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
/**\defgroup stream_info Stream Information*/
/*@{*/
/**@{*/
/**\name Functions for obtaining information about streams
These functions allow you to get basic information about a stream, including
@@ -1437,18 +1425,17 @@ void op_free(OggOpusFile *_of);
streams returned by op_test_callbacks() or one of the associated
convenience functions.
Their documention will indicate so explicitly.*/
/*@{*/
/**@{*/
/**Returns whether or not the stream being read is seekable.
This is true if
<ol>
<li>The <code><a href="#op_seek_func">seek()</a></code> and
<code><a href="#op_tell_func">tell()</a></code> callbacks are both
non-<code>NULL</code>,</li>
<li>The <code><a href="#op_seek_func">seek()</a></code> callback was
successfully executed at least once, and</li>
<li>The <code><a href="#op_tell_func">tell()</a></code> callback was
successfully able to report the position indicator afterwards.</li>
<li>The \ref op_seek_func "seek()" and \ref op_tell_func "tell()"
callbacks are both non-<code>NULL</code>,</li>
<li>The \ref op_seek_func "seek()" callback was successfully executed at
least once, and</li>
<li>The \ref op_tell_func "tell()" callback was successfully able to report
the position indicator afterwards.</li>
</ol>
This function may be called on partially-opened streams.
\param _of The \c OggOpusFile whose seekable status is to be returned.
@@ -1638,11 +1625,11 @@ opus_int64 op_raw_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1);
\retval #OP_EINVAL The stream was only partially open.*/
ogg_int64_t op_pcm_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
/**\defgroup stream_seeking Seeking*/
/*@{*/
/**@{*/
/**\name Functions for seeking in Opus streams
These functions let you seek in Opus streams, if the underlying stream
@@ -1667,7 +1654,7 @@ ogg_int64_t op_pcm_tell(const OggOpusFile *_of) OP_ARG_NONNULL(1);
values as would be obtained by decoding the stream straight through.
However, such differences are expected to be smaller than the loss
introduced by Opus's lossy compression.*/
/*@{*/
/**@{*/
/**Seek to a byte offset relative to the <b>compressed</b> data.
This also scans packets to update the PCM cursor.
@@ -1702,11 +1689,11 @@ int op_raw_seek(OggOpusFile *_of,opus_int64 _byte_offset) OP_ARG_NONNULL(1);
seeking to the target destination was impossible.*/
int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
/**\defgroup stream_decoding Decoding*/
/*@{*/
/**@{*/
/**\name Functions for decoding audio data
These functions retrieve actual decoded audio data from the stream.
@@ -1744,7 +1731,7 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
If you are reading from an <https:> URL (particularly if seeking is not
supported), you should make sure to check for this error and warn the user
appropriately.*/
/*@{*/
/**@{*/
/**Indicates that the decoding callback should produce signed 16-bit
native-endian output samples.*/
@@ -1890,7 +1877,7 @@ void op_set_dither_enabled(OggOpusFile *_of,int _enabled) OP_ARG_NONNULL(1);
signed native-endian 16-bit values at 48&nbsp;kHz
with a nominal range of <code>[-32768,32767)</code>.
Multiple channels are interleaved using the
<a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis
<a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810004.3.9">Vorbis
channel ordering</a>.
This must have room for at least \a _buf_size values.
\param _buf_size The number of values that can be stored in \a _pcm.
@@ -1972,7 +1959,7 @@ OP_WARN_UNUSED_RESULT int op_read(OggOpusFile *_of,
signed floats at 48&nbsp;kHz with a nominal range of
<code>[-1.0,1.0]</code>.
Multiple channels are interleaved using the
<a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis
<a href="https://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-810004.3.9">Vorbis
channel ordering</a>.
This must have room for at least \a _buf_size floats.
\param _buf_size The number of floats that can be stored in \a _pcm.
@@ -2150,8 +2137,8 @@ OP_WARN_UNUSED_RESULT int op_read_stereo(OggOpusFile *_of,
OP_WARN_UNUSED_RESULT int op_read_float_stereo(OggOpusFile *_of,
float *_pcm,int _buf_size) OP_ARG_NONNULL(1);
/*@}*/
/*@}*/
/**@}*/
/**@}*/
# if OP_GNUC_PREREQ(4,0)
# pragma GCC visibility pop

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -136,7 +136,7 @@ const static actrls_t controls = {
NULL, NULL, // rew, fwd
bt_prev, bt_next, // prev, next
NULL, NULL, NULL, NULL, // left, right, up, down
NULL, NULL, NULL, NULL, NULL, NULL, // pre1-6
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // pre1-10
bt_volume_down, bt_volume_up, bt_toggle// knob left, knob_right, knob push
};

View File

@@ -121,7 +121,7 @@ void config_start_timer(){
nvs_type_t config_get_item_type(cJSON * entry){
if(entry==NULL){
ESP_LOGE(TAG,"null pointer received!");
return true;
return 0;
}
cJSON * item_type = cJSON_GetObjectItemCaseSensitive(entry, "type");
if(item_type ==NULL ) {
@@ -142,7 +142,7 @@ cJSON * config_set_value_safe(nvs_type_t nvs_type, const char *key, const void
return NULL;
}
cJSON * existing = cJSON_GetObjectItemCaseSensitive(nvs_json, key);
cJSON * existing = cJSON_GetObjectItemCaseSensitive(nvs_json, key);
if(existing !=NULL && nvs_type == NVS_TYPE_STR && config_get_item_type(existing) != NVS_TYPE_STR ) {
ESP_LOGW(TAG, "Storing numeric value from string");
numvalue = atof((char *)value);

View File

@@ -40,7 +40,8 @@ const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
#endif
};
extern int main(int argc, char **argv);
extern int squeezelite_main(int argc, char **argv);
static int launchsqueezelite(int argc, char **argv);
/** Arguments used by 'squeezelite' function */
@@ -54,39 +55,32 @@ static struct {
} thread_parms ;
#define ADDITIONAL_SQUEEZELITE_ARGS 5
static void squeezelite_thread(void *arg){
static void squeezelite_thread(void *arg){
ESP_LOGV(TAG ,"Number of args received: %u",thread_parms.argc );
ESP_LOGV(TAG ,"Values:");
for(int i = 0;i<thread_parms.argc; i++){
ESP_LOGV(TAG ," %s",thread_parms.argv[i]);
}
ESP_LOGI(TAG ,"Calling squeezelite");
int ret = main(thread_parms.argc,thread_parms.argv);
ESP_LOGV(TAG ,"Exited from squeezelite's main(). Freeing argv structure.");
for(int i=0;i<thread_parms.argc;i++){
ESP_LOGV(TAG ,"Freeing char buffer for parameter %u", i+1);
free(thread_parms.argv[i]);
}
ESP_LOGV(TAG ,"Freeing argv pointer");
free(thread_parms.argv);
if(!wait_for_commit()){
ESP_LOGW(TAG,"Unable to commit configuration. ");
}
messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "squeezelite exited with error code %d", ret);
ESP_LOGI(TAG ,"Calling squeezelite");
int ret = squeezelite_main(thread_parms.argc, thread_parms.argv);
cmd_send_messaging("cfg-audio-tmpl",ret > 1 ? MESSAGING_ERROR : MESSAGING_WARNING,"squeezelite exited with error code %d\n", ret);
if (ret == 1) {
int wait = 60;
messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "Rebooting in %d sec", wait);
wait_for_commit();
cmd_send_messaging("cfg-audio-tmpl",MESSAGING_ERROR,"Rebooting in %d sec\n", wait);
vTaskDelay( pdMS_TO_TICKS(wait * 1000));
esp_restart();
} else {
messaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, "Correct command line and reboot");
cmd_send_messaging("cfg-audio-tmpl",MESSAGING_ERROR,"Correct command line and reboot\n");
vTaskSuspend(NULL);
}
ESP_LOGV(TAG, "Exited from squeezelite's main(). Freeing argv structure.");
for(int i=0;i<thread_parms.argc;i++) free(thread_parms.argv[i]);
free(thread_parms.argv);
}
static int launchsqueezelite(int argc, char **argv) {

View File

@@ -50,6 +50,7 @@ static const dmap_field dmap_fields[] = {
{ "abar", DMAP_DICT, DMAP_STR, "daap.browseartistlisting" },
{ "abcp", DMAP_DICT, DMAP_STR, "daap.browsecomposerlisting" },
{ "abgn", DMAP_DICT, DMAP_STR, "daap.browsegenrelisting" },
#ifdef DMAP_FULL
{ "abpl", DMAP_UINT, 0, "daap.baseplaylist" },
{ "abro", DMAP_DICT, 0, "daap.databasebrowse" },
{ "adbs", DMAP_DICT, 0, "daap.databasesongs" },
@@ -256,10 +257,12 @@ static const dmap_field dmap_fields[] = {
{ "meia", DMAP_UINT, 0, "dmap.itemdateadded" },
{ "meip", DMAP_UINT, 0, "dmap.itemdateplayed" },
{ "mext", DMAP_UINT, 0, "dmap.objectextradata" },
#endif
{ "miid", DMAP_UINT, 0, "dmap.itemid" },
{ "mikd", DMAP_UINT, 0, "dmap.itemkind" },
{ "mimc", DMAP_UINT, 0, "dmap.itemcount" },
{ "minm", DMAP_STR, 0, "dmap.itemname" },
#ifdef DMAP_FULL
{ "mlcl", DMAP_DICT, DMAP_DICT, "dmap.listing" },
{ "mlid", DMAP_UINT, 0, "dmap.sessionid" },
{ "mlit", DMAP_ITEM, 0, "dmap.listingitem" },
@@ -314,6 +317,7 @@ static const dmap_field dmap_fields[] = {
{ "prat", DMAP_UINT, 0, "dpap.imagerating" },
{ "pret", DMAP_DICT, 0, "dpap.retryids" },
{ "pwth", DMAP_UINT, 0, "dpap.imagepixelwidth" }
#endif
};
static const size_t dmap_field_count = sizeof(dmap_fields) / sizeof(dmap_field);

View File

@@ -959,7 +959,7 @@ static int base64_decode(const char *str, void *data)
*q++ = (val >> 8) & 0xff;
if (marker < 1)
*q++ = val & 0xff;
}
}
return q - (unsigned char *) data;
}

View File

@@ -90,7 +90,7 @@ const static actrls_t controls = {
NULL, NULL, // rew, fwd
raop_prev, raop_next, // prev, next
NULL, NULL, NULL, NULL, // left, right, up, down
NULL, NULL, NULL, NULL, NULL, NULL, // pre1-6
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // pre1-10
raop_volume_down, raop_volume_up, raop_toggle// knob left, knob_right, knob push
};

View File

@@ -71,7 +71,8 @@ static log_level *loglevel = &raop_loglevel;
//#define __RTP_STORE
// default buffer size
#define BUFFER_FRAMES ( (150 * RAOP_SAMPLE_RATE * 2) / (352 * 100) )
#define BUFFER_FRAMES_MAX ((RAOP_SAMPLE_RATE * 10) / 352 )
#define BUFFER_FRAMES_MIN ( (150 * RAOP_SAMPLE_RATE * 2) / (352 * 100) )
#define MAX_PACKET 1408
#define MIN_LATENCY 11025
#define MAX_LATENCY ( (120 * RAOP_SAMPLE_RATE * 2) / 100 )
@@ -86,14 +87,15 @@ static log_level *loglevel = &raop_loglevel;
enum { DATA = 0, CONTROL, TIMING };
static const u8_t silence_frame[MAX_PACKET] = { 0 };
uint32_t buffer_frames = ((150 * RAOP_SAMPLE_RATE * 2) / (352 * 100));
typedef u16_t seq_t;
typedef struct audio_buffer_entry { // decoded audio packets
int ready;
typedef struct __attribute__((__packed__)) audio_buffer_entry { // decoded audio packets
u32_t rtptime, last_resend;
s16_t *data;
int len;
bool allocated;
u16_t len;
u8_t ready;
u8_t allocated;
} abuf_t;
typedef struct rtp_s {
@@ -133,7 +135,7 @@ typedef struct rtp_s {
u32_t resent_req, resent_rec; // total resent + recovered frames
u32_t silent_frames; // total silence frames
u32_t discarded;
abuf_t audio_buffer[BUFFER_FRAMES];
abuf_t audio_buffer[BUFFER_FRAMES_MAX];
seq_t ab_read, ab_write;
pthread_mutex_t ab_mutex;
#ifdef WIN32
@@ -152,7 +154,7 @@ typedef struct rtp_s {
} rtp_t;
#define BUFIDX(seqno) ((seq_t)(seqno) % BUFFER_FRAMES)
#define BUFIDX(seqno) ((seq_t)(seqno) % buffer_frames)
static void buffer_alloc(abuf_t *audio_buffer, int size, uint8_t *buf, size_t buf_size);
static void buffer_release(abuf_t *audio_buffer);
static void buffer_reset(abuf_t *audio_buffer);
@@ -373,25 +375,27 @@ void rtp_record(rtp_t *ctx, unsigned short seqno, unsigned rtptime) {
/*---------------------------------------------------------------------------*/
static void buffer_alloc(abuf_t *audio_buffer, int size, uint8_t *buf, size_t buf_size) {
int i;
for (i = 0; i < BUFFER_FRAMES; i++) {
if (buf && buf_size >= size) {
audio_buffer[i].data = (s16_t*) buf;
audio_buffer[i].allocated = false;
buf += size;
buf_size -= size;
} else {
audio_buffer[i].allocated = true;
audio_buffer[i].data = malloc(size);
}
audio_buffer[i].ready = 0;
for (buffer_frames = 0; buf && buf_size >= size && buffer_frames < BUFFER_FRAMES_MAX; buffer_frames++) {
audio_buffer[buffer_frames].data = (s16_t*) buf;
audio_buffer[buffer_frames].allocated = 0;
audio_buffer[buffer_frames].ready = 0;
buf += size;
buf_size -= size;
}
LOG_INFO("allocated %d buffers (min=%d) from buffer of %zu bytes", buffer_frames, BUFFER_FRAMES_MIN, buf_size + buffer_frames * size);
for(; buffer_frames < BUFFER_FRAMES_MIN; buffer_frames++) {
audio_buffer[buffer_frames].data = malloc(size);
audio_buffer[buffer_frames].allocated = 1;
audio_buffer[buffer_frames].ready = 0;
}
}
/*---------------------------------------------------------------------------*/
static void buffer_release(abuf_t *audio_buffer) {
int i;
for (i = 0; i < BUFFER_FRAMES; i++) {
for (i = 0; i < buffer_frames; i++) {
if (audio_buffer[i].allocated) free(audio_buffer[i].data);
}
}
@@ -399,7 +403,7 @@ static void buffer_release(abuf_t *audio_buffer) {
/*---------------------------------------------------------------------------*/
static void buffer_reset(abuf_t *audio_buffer) {
int i;
for (i = 0; i < BUFFER_FRAMES; i++) audio_buffer[i].ready = 0;
for (i = 0; i < buffer_frames; i++) audio_buffer[i].ready = 0;
}
/*---------------------------------------------------------------------------*/
@@ -411,7 +415,7 @@ static int seq_order(seq_t a, seq_t b) {
}
/*---------------------------------------------------------------------------*/
static void alac_decode(rtp_t *ctx, s16_t *dest, char *buf, int len, int *outsize) {
static void alac_decode(rtp_t *ctx, s16_t *dest, char *buf, int len, u16_t *outsize) {
unsigned char iv[16];
int aeslen;
assert(len<=MAX_PACKET);
@@ -803,7 +807,7 @@ static bool rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last) {
static bool rtp_request_resend(rtp_t *ctx, seq_t first, seq_t last) {
unsigned char req[8]; // *not* a standard RTCP NACK
// do not request silly ranges (happens in case of network large blackouts)
// do not request silly ranges (happens in case of network large blackouts)
if (seq_order(last, first) || last - first > buffer_frames / 2) return false;
ctx->resent_req += (seq_t) (last - first) + 1;

View File

@@ -60,7 +60,7 @@ static const actrls_config_map_t actrls_config_map[] =
static const char * actrls_action_s[ ] = { EP(ACTRLS_POWER),EP(ACTRLS_VOLUP),EP(ACTRLS_VOLDOWN),EP(ACTRLS_TOGGLE),EP(ACTRLS_PLAY),
EP(ACTRLS_PAUSE),EP(ACTRLS_STOP),EP(ACTRLS_REW),EP(ACTRLS_FWD),EP(ACTRLS_PREV),EP(ACTRLS_NEXT),
EP(BCTRLS_UP),EP(BCTRLS_DOWN),EP(BCTRLS_LEFT),EP(BCTRLS_RIGHT),
EP(BCTRLS_PS1),EP(BCTRLS_PS2),EP(BCTRLS_PS3),EP(BCTRLS_PS4),EP(BCTRLS_PS5),EP(BCTRLS_PS6),
EP(BCTRLS_PS0),EP(BCTRLS_PS1),EP(BCTRLS_PS2),EP(BCTRLS_PS3),EP(BCTRLS_PS4),EP(BCTRLS_PS5),EP(BCTRLS_PS6),EP(BCTRLS_PS7),EP(BCTRLS_PS8),EP(BCTRLS_PS9),
EP(KNOB_LEFT),EP(KNOB_RIGHT),EP(KNOB_PUSH),
""} ;

View File

@@ -14,7 +14,7 @@
typedef enum { ACTRLS_NONE = -1, ACTRLS_POWER,ACTRLS_VOLUP, ACTRLS_VOLDOWN, ACTRLS_TOGGLE, ACTRLS_PLAY,
ACTRLS_PAUSE, ACTRLS_STOP, ACTRLS_REW, ACTRLS_FWD, ACTRLS_PREV, ACTRLS_NEXT,
BCTRLS_UP, BCTRLS_DOWN, BCTRLS_LEFT, BCTRLS_RIGHT,
BCTRLS_PS1,BCTRLS_PS2,BCTRLS_PS3,BCTRLS_PS4,BCTRLS_PS5,BCTRLS_PS6,
BCTRLS_PS0,BCTRLS_PS1,BCTRLS_PS2,BCTRLS_PS3,BCTRLS_PS4,BCTRLS_PS5,BCTRLS_PS6,BCTRLS_PS7,BCTRLS_PS8,BCTRLS_PS9,
KNOB_LEFT, KNOB_RIGHT, KNOB_PUSH,
ACTRLS_REMAP, ACTRLS_MAX
} actrls_action_e;

View File

@@ -219,18 +219,23 @@ esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_m
}
return ESP_LOG_DEBUG;
}
void messaging_post_message(messaging_types type,messaging_classes msg_class, const char *fmt, ...){
va_list va;
va_start(va, fmt);
vmessaging_post_message(type, msg_class, fmt, va);
va_end(va);
}
void vmessaging_post_message(messaging_types type,messaging_classes msg_class, const char *fmt, va_list va){
single_message_t * message=NULL;
size_t msg_size=0;
size_t ln =0;
messaging_list_t * cur=&top;
va_list va;
va_start(va, fmt);
ln = vsnprintf(NULL, 0, fmt, va)+1;
msg_size = sizeof(single_message_t)+ln;
message = (single_message_t *)malloc_init_external(msg_size);
vsprintf(message->message, fmt, va);
va_end(va);
message->msg_size = msg_size;
message->type = type;
message->msg_class = msg_class;

View File

@@ -34,6 +34,7 @@ cJSON * messaging_retrieve_messages(RingbufHandle_t buf_handle);
messaging_handle_t messaging_register_subscriber(uint8_t max_count, char * name);
esp_err_t messaging_post_to_queue(messaging_handle_t subscriber_handle, single_message_t * message, size_t message_size);
void messaging_post_message(messaging_types type,messaging_classes msg_class, const char * fmt, ...);
void vmessaging_post_message(messaging_types type,messaging_classes msg_class, const char *fmt, va_list va);
cJSON * messaging_retrieve_messages(RingbufHandle_t buf_handle);
single_message_t * messaging_retrieve_message(RingbufHandle_t buf_handle);
void log_send_messaging(messaging_types msgtype,const char *fmt, ...);

View File

@@ -443,11 +443,11 @@ void cspotPlayer::runTask() {
CSPOT_LOG(info, "disconnecting player %s", name.c_str());
}
// we want to release memory ASAP and fore sure
// we want to release memory ASAP and for sure
centralAudioBuffer.reset();
ctx.reset();
token.clear();
// update volume when we disconnect
cJSON *config = config_alloc_get_cjson("cspot_config");
cJSON_DeleteItemFromObject(config, "volume");

View File

@@ -120,9 +120,9 @@ void MercurySession::handlePacket() {
switch (static_cast<RequestType>(packet.command)) {
case RequestType::COUNTRY_CODE_RESPONSE: {
this->countryCode = std::string();
this->countryCode.reserve(2);
this->countryCode.resize(2);
memcpy(this->countryCode.data(), packet.data.data(), 2);
CSPOT_LOG(debug, "Received country code");
CSPOT_LOG(debug, "Received country code %s", this->countryCode.c_str());
break;
}
case RequestType::AUDIO_KEY_FAILURE_RESPONSE:

View File

@@ -77,12 +77,11 @@ void TrackProvider::onMetadataResponse(MercurySession::Response& res) {
std::vector<uint8_t> trackId;
std::vector<uint8_t> fileId;
AudioFormat format = AudioFormat_OGG_VORBIS_160;
if (altIndex < 0) {
trackId = pbArrayToVector(trackInfo.gid);
for (int x = 0; x < trackInfo.file_count; x++) {
if (trackInfo.file[x].format == format) {
if (trackInfo.file[x].format == ctx->config.audioFormat) {
fileId = pbArrayToVector(trackInfo.file[x].file_id);
break; // If file found stop searching
}
@@ -90,7 +89,7 @@ void TrackProvider::onMetadataResponse(MercurySession::Response& res) {
} else {
trackId = pbArrayToVector(trackInfo.alternative[altIndex].gid);
for (int x = 0; x < trackInfo.alternative[altIndex].file_count; x++) {
if (trackInfo.alternative[altIndex].file[x].format == format) {
if (trackInfo.alternative[altIndex].file[x].format == ctx->config.audioFormat) {
fileId =
pbArrayToVector(trackInfo.alternative[altIndex].file[x].file_id);
break; // If file found stop searching

View File

@@ -85,7 +85,7 @@ const static actrls_t controls = {
NULL, NULL, // rew, fwd
cspot_prev, cspot_next, // prev, next
NULL, NULL, NULL, NULL, // left, right, up, down
NULL, NULL, NULL, NULL, NULL, NULL, // pre1-6
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, // pre1-10
cspot_volume_down, cspot_volume_up, cspot_toggle// knob left, knob_right, knob push
};

View File

@@ -13,6 +13,7 @@ idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978
display
tools
audio
_override
EMBED_FILES vu_s.data arrow.data
)

View File

@@ -21,7 +21,8 @@ static log_level loglevel = lINFO;
enum { BUTN_POWER_FRONT = 0X0A, BUTN_ARROW_UP, BUTN_ARROW_DOWN, BUTN_ARROW_LEFT, BUTN_KNOB_PUSH, BUTN_SEARCH,
BUTN_REW, BUTN_FWD, BUTN_PLAY, BUTN_ADD, BUTN_BRIGHTNESS, BUTN_NOW_PLAYING,
BUTN_PAUSE = 0X17, BUTN_BROWSE, BUTN_VOLUP_FRONT, BUTN_VOLDOWN_FRONT, BUTN_SIZE, BUTN_VISUAL, BUTN_VOLUMEMODE,
BUTN_PRESET_1 = 0X23, BUTN_PRESET_2, BUTN_PRESET_3, BUTN_PRESET_4, BUTN_PRESET_5, BUTN_PRESET_6, BUTN_SNOOZE,
BUTN_PRESET_0 = 0x22, BUTN_PRESET_1, BUTN_PRESET_2, BUTN_PRESET_3, BUTN_PRESET_4, BUTN_PRESET_5, BUTN_PRESET_6, BUTN_PRESET_7, BUTN_PRESET_8, BUTN_PRESET_9,
BUTN_SNOOZE,
BUTN_KNOB_LEFT = 0X5A, BUTN_KNOB_RIGHT };
#define BUTN_ARROW_RIGHT BUTN_KNOB_PUSH
@@ -142,12 +143,16 @@ LMS_CALLBACK(down, ARROW_DOWN, arrow_down)
LMS_CALLBACK(left, ARROW_LEFT, arrow_left)
LMS_CALLBACK(right, ARROW_RIGHT, arrow_right)
LMS_CALLBACK(pre0, PRESET_0, preset_0.single)
LMS_CALLBACK(pre1, PRESET_1, preset_1.single)
LMS_CALLBACK(pre2, PRESET_2, preset_2.single)
LMS_CALLBACK(pre3, PRESET_3, preset_3.single)
LMS_CALLBACK(pre4, PRESET_4, preset_4.single)
LMS_CALLBACK(pre5, PRESET_5, preset_5.single)
LMS_CALLBACK(pre6, PRESET_6, preset_6.single)
LMS_CALLBACK(pre7, PRESET_7, preset_7.single)
LMS_CALLBACK(pre8, PRESET_8, preset_8.single)
LMS_CALLBACK(pre9, PRESET_9, preset_9.single)
LMS_CALLBACK(knob_left, KNOB_LEFT, knob_left)
LMS_CALLBACK(knob_right, KNOB_RIGHT, knob_right)
@@ -162,7 +167,7 @@ const actrls_t LMS_controls = {
lms_prev, lms_next, // prev, next
lms_up, lms_down,
lms_left, lms_right,
lms_pre1, lms_pre2, lms_pre3, lms_pre4, lms_pre5, lms_pre6,
lms_pre0, lms_pre1, lms_pre2, lms_pre3, lms_pre4, lms_pre5, lms_pre6, lms_pre7, lms_pre8, lms_pre9,
lms_knob_left, lms_knob_right, lms_knob_push,
};

View File

@@ -0,0 +1,561 @@
/*
* Squeezelite for esp32
*
* (c) Sebastien 2019
* Philippe G. 2019, philippe_44@outlook.com
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*
*/
#include <string.h>
//#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s.h"
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "adac.h"
#include "stdio.h"
#include "math.h"
#define CS4265_PULL_UP (0x4F )
#define CS4265_PULL_DOWN (0x4E )
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
static const char TAG[] = "CS4265";
static bool init(char *config, int i2c_port_num, i2s_config_t *i2s_config);
static void speaker(bool active);
static void headset(bool active);
static bool volume(unsigned left, unsigned right);
static void power(adac_power_e mode);
static esp_err_t cs4265_update_bit(uint8_t reg_no,uint8_t mask,uint8_t val );
static esp_err_t set_clock();
const struct adac_s dac_cs4265 = { "CS4265", init, adac_deinit, power, speaker, headset, volume };
struct cs4265_cmd_s {
uint8_t reg;
uint8_t value;
};
struct cs4265_private {
uint8_t format;
uint32_t sysclk;
i2s_config_t *i2s_config;
int i2c_port;
};
struct cs4265_private cs4265;
#define CS4265_CHIP_ID 0x1
#define CS4265_CHIP_ID_VAL 0xD0
#define CS4265_CHIP_ID_MASK 0xF0
#define CS4265_REV_ID_MASK 0x0F
#define CS4265_PWRCTL 0x02
#define CS4265_PWRCTL_PDN (1 << 0)
#define CS4265_PWRCTL_PDN_DAC (1 << 1)
#define CS4265_PWRCTL_PDN_ADC (1 << 2)
#define CS4265_PWRCTL_PDN_MIC (1 << 3)
#define CS4265_PWRCTL_FREEZE (1 << 7)
#define CS4265_PWRCTL_PDN_ALL CS4265_PWRCTL_PDN | CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_PDN_DAC | CS4265_PWRCTL_PDN_MIC
#define CS4265_DAC_CTL 0x3
// De-Emphasis Control (Bit 1)
// The standard 50/15 i2s digital de-emphasis filter response may be implemented for a sample
// rate of 44.1 kHz when the DeEmph bit is set. NOTE: De-emphasis is available only in Single-Speed Mode.
#define CS4265_DAC_CTL_DEEMPH (1 << 1)
// MUTE DAC
// The DAC outputs will mute and the MUTEC pin will become active when this bit is set. Though this bit is
// active high, it should be noted that the MUTEC pin is active low. The common mode voltage on the outputs
// will be retained when this bit is set. The muting function is effected, similar to attenuation changes, by the
// DACSoft and DACZero bits in the DAC Control 2 register.
#define CS4265_DAC_CTL_MUTE (1 << 2)
// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital Interface
// DAC_DIF1 DAC_DIF0 Description Format Figure
// 0 0 Left Justified, up to 24-bit data (default) 0 5
// 0 1 I²S, up to 24-bit data 1 6
// 1 0 Right-Justified, 16-bit Data 2 7
// 1 1 Right-Justified, 24-bit Data 3 7
#define CS4265_DAC_CTL_DIF0 (1 << 4)
// The required relationship between LRCK, SCLK and SDIN for the DAC is defined by the DAC Digital Interface
// DAC_DIF1 DAC_DIF0 Description Format Figure
// 0 0 Left Justified, up to 24-bit data (default) 0 5
// 0 1 I²S, up to 24-bit data 1 6
// 1 0 Right-Justified, 16-bit Data 2 7
// 1 1 Right-Justified, 24-bit Data 3 7
#define CS4265_DAC_CTL_DIF1 (1 << 5)
#define CS4265_ADC_CTL 0x4
#define CS4265_ADC_MASTER 1
#define CS4265_ADC_CTL_MUTE (1 << 2)
#define CS4265_ADC_DIF (1 << 4)
#define CS4265_ADC_FM (3 << 6)
//Master Clock Dividers (Bits 6:4)
//Sets the frequency of the supplied MCLK signal.
//
//MCLK Divider MCLK Freq2 MCLK Freq1 MCLK Freq0
// ÷ 1 0 0 0
// ÷ 1.5 0 0 1
// ÷ 2 0 1 0
// ÷ 3 0 1 1
// ÷ 4 1 0 0
// NA 1 0 1
// NA 1 1 x
#define CS4265_MCLK_FREQ 0x5
#define CS4265_MCLK_FREQ_1_0X (0b000<<4 )
#define CS4265_MCLK_FREQ_1_5X (0b001<<4 )
#define CS4265_MCLK_FREQ_2_0X (0b010<<4 )
#define CS4265_MCLK_FREQ_3_0X (0b011<<4 )
#define CS4265_MCLK_FREQ_4_0X (0b100<<4 )
#define CS4265_MCLK_FREQ_MASK (7 << 4)
#define CS4265_SIG_SEL 0x6
#define CS4265_SIG_SEL_LOOP (1 << 1)
#define CS4265_SIG_SEL_SDIN2 (1 << 7)
#define CS4265_SIG_SEL_SDIN1 (0 << 7)
// Sets the gain or attenuation for the ADC input PGA stage. The gain may be adjusted from -12 dB to
// +12 dB in 0.5 dB steps. The gain bits are in twos complement with the Gain0 bit set for a 0.5 dB step.
// Register settings outside of the ±12 dB range are reserved and must not be used. See Table 13 for example settings
#define CS4265_CHB_PGA_CTL 0x7
// Sets the gain or attenuation for the ADC input PGA stage. The gain may be adjusted from -12 dB to
// +12 dB in 0.5 dB steps. The gain bits are in twos complement with the Gain0 bit set for a 0.5 dB step.
// Register settings outside of the ±12 dB range are reserved and must not be used. See Table 13 for example settings
#define CS4265_CHA_PGA_CTL 0x8
// Gain[5:0] Setting
// 101000 -12 dB
// 000000 0 dB
// 011000 +12 dB
#define CS4265_ADC_CTL2 0x9
// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to -127 dB.
// The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared. The Vol[7:1] bits
// activate attenuation equal to their decimal equivalent (in dB).
//Binary Code Volume Setting
//00000000 0 dB
//00000001 -0.5 dB
//00101000 -20 dB
//00101001 -20.5 dB
//11111110 -127 dB
//11111111 -127.5 dB
#define CS4265_DAC_CHA_VOL 0xA
// The digital volume control allows the user to attenuate the signal in 0.5 dB increments from 0 to -127 dB.
// The Vol0 bit activates a 0.5 dB attenuation when set, and no attenuation when cleared. The Vol[7:1] bits
// activate attenuation equal to their decimal equivalent (in dB).
//Binary Code Volume Setting
//00000000 0 dB
//00000001 -0.5 dB
//00101000 -20 dB
//00101001 -20.5 dB
//11111110 -127 dB
//11111111 -127.5 dB
#define CS4265_DAC_CHB_VOL 0xB
#define CS4265_DAC_VOL_ATT_000_0 0b00000000
#define CS4265_DAC_VOL_ATT_000_5 0b00000001
#define CS4265_DAC_VOL_ATT_020_0 0b00101000
#define CS4265_DAC_VOL_ATT_020_5 0b00101001
#define CS4265_DAC_VOL_ATT_127_0 0b11111110
#define CS4265_DAC_VOL_ATT_127_5 0b11111111
// DAC Soft Ramp or Zero Cross Enable (Bits 7:6)
//
// Soft Ramp Enable
// Soft Ramp allows level changes, both muting and attenuation, to be implemented by incrementally ramping, in 1/8 dB steps, from the current level to the new level at a rate of 1 dB per 8 left/right clock periods.
// See Table 17.
// Zero Cross Enable
// Zero Cross Enable dictates that signal-level changes, either by attenuation changes or muting, will occur
// on a signal zero crossing to minimize audible artifacts. The requested level change will occur after a timeout period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz sample rate) if the signal
// does not encounter a zero crossing. The zero cross function is independently monitored and implemented
// for each channel. See Table 17.
// Soft Ramp and Zero Cross Enable
// Soft Ramp and Zero Cross Enable dictate that signal-level changes, either by attenuation changes or muting, will occur in 1/8 dB steps and be implemented on a signal zero crossing. The 1/8 dB level change will
// occur after a time-out period between 512 and 1024 sample periods (10.7 ms to 21.3 ms at 48 kHz sample rate) if the signal does not encounter a zero crossing. The zero cross function is independently monitored and implemented for each channel
// DACSoft DACZeroCross Mode
// 0 0 Changes to affect immediately
// 0 1 Zero Cross enabled
// 1 0 Soft Ramp enabled
// 1 1 Soft Ramp and Zero Cross enabled (default)
#define CS4265_DAC_CTL2 0xC
#define CS4265_DAC_CTL2_ZERO_CROSS_EN (uint8_t)(0b01 <<7)
#define CS4265_DAC_CTL2_SOFT_RAMP_EN (uint8_t)(0b10 <<7)
#define CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN (uint8_t)(0b11 <<7)
#define CS4265_INT_STATUS 0xD
#define CS4265_INT_STATUS_ADC_UNDF (1<<0)
#define CS4265_INT_STATUS_ADC_OVF (1<<1)
#define CS4265_INT_STATUS_CLKERR (1<<3)
#define CS4265_INT_MASK 0xE
#define CS4265_STATUS_MODE_MSB 0xF
#define CS4265_STATUS_MODE_LSB 0x10
//Transmitter Control 1 - Address 11h
#define CS4265_SPDIF_CTL1 0x11
#define CS4265_SPDIF_CTL2 0x12
// Transmitter Digital Interface Format (Bits 7:6)
// Function:
// The required relationship between LRCK, SCLK and SDIN for the transmitter is defined
// Tx_DIF1 Tx_DIF0 Description Format Figure
// 0 0 Left Justified, up to 24-bit data (default) 0 5
// 0 1 I²S, up to 24-bit data 1 6
// 1 0 Right-Justified, 16-bit Data 2 7
// 1 1 Right-Justified, 24-bit Data 3 7
#define CS4265_SPDIF_CTL2_MMTLR (1<<0)
#define CS4265_SPDIF_CTL2_MMTCS (1<<1)
#define CS4265_SPDIF_CTL2_MMT (1<<2)
#define CS4265_SPDIF_CTL2_V (1<<3)
#define CS4265_SPDIF_CTL2_TXMUTE (1<<4)
#define CS4265_SPDIF_CTL2_TXOFF (1<<5)
#define CS4265_SPDIF_CTL2_MUTE (1 << 4)
#define CS4265_SPDIF_CTL2_DIF (3 << 6)
#define CS4265_SPDIF_CTL2_DIF0 (1 << 6)
#define CS4265_SPDIF_CTL2_DIF1 (1 << 7)
#define CS4265_C_DATA_BUFF 0x13
#define CS4265_MAX_REGISTER 0x2A
struct cs4265_clk_para {
uint32_t mclk;
uint32_t rate;
uint8_t fm_mode; /* values 1, 2, or 4 */
uint8_t mclkdiv;
};
static const struct cs4265_clk_para clk_map_table[] = {
/*32k*/
{8192000, 32000, 0, 0},
{12288000, 32000, 0, 1},
{16384000, 32000, 0, 2},
{24576000, 32000, 0, 3},
{32768000, 32000, 0, 4},
/*44.1k*/
{11289600, 44100, 0, 0},
{16934400, 44100, 0, 1},
{22579200, 44100, 0, 2},
{33868000, 44100, 0, 3},
{45158400, 44100, 0, 4},
/*48k*/
{12288000, 48000, 0, 0},
{18432000, 48000, 0, 1},
{24576000, 48000, 0, 2},
{36864000, 48000, 0, 3},
{49152000, 48000, 0, 4},
/*64k*/
{8192000, 64000, 1, 0},
{12288000, 64000, 1, 1},
{16934400, 64000, 1, 2},
{24576000, 64000, 1, 3},
{32768000, 64000, 1, 4},
/* 88.2k */
{11289600, 88200, 1, 0},
{16934400, 88200, 1, 1},
{22579200, 88200, 1, 2},
{33868000, 88200, 1, 3},
{45158400, 88200, 1, 4},
/* 96k */
{12288000, 96000, 1, 0},
{18432000, 96000, 1, 1},
{24576000, 96000, 1, 2},
{36864000, 96000, 1, 3},
{49152000, 96000, 1, 4},
/* 128k */
{8192000, 128000, 2, 0},
{12288000, 128000, 2, 1},
{16934400, 128000, 2, 2},
{24576000, 128000, 2, 3},
{32768000, 128000, 2, 4},
/* 176.4k */
{11289600, 176400, 2, 0},
{16934400, 176400, 2, 1},
{22579200, 176400, 2, 2},
{33868000, 176400, 2, 3},
{49152000, 176400, 2, 4},
/* 192k */
{12288000, 192000, 2, 0},
{18432000, 192000, 2, 1},
{24576000, 192000, 2, 2},
{36864000, 192000, 2, 3},
{49152000, 192000, 2, 4},
};
static const struct cs4265_cmd_s cs4265_init_sequence[] = {
{CS4265_PWRCTL, CS4265_PWRCTL_PDN_ADC | CS4265_PWRCTL_FREEZE | CS4265_PWRCTL_PDN_DAC | CS4265_PWRCTL_PDN_MIC},
{CS4265_DAC_CTL, CS4265_DAC_CTL_DIF0 | CS4265_DAC_CTL_MUTE},
{CS4265_SIG_SEL, CS4265_SIG_SEL_SDIN1},/// SDIN1
{CS4265_SPDIF_CTL2, CS4265_SPDIF_CTL2_DIF0 },//
{CS4265_ADC_CTL, 0x00 },// // Set the serial audio port in slave mode
{CS4265_MCLK_FREQ, CS4265_MCLK_FREQ_1_0X },// // no divider
{CS4265_CHB_PGA_CTL, 0x00 },// // sets the gain to 0db on channel B
{CS4265_CHA_PGA_CTL, 0x00 },// // sets the gain to 0db on channel A
{CS4265_ADC_CTL2, 0x19 },//
{CS4265_DAC_CHA_VOL,CS4265_DAC_VOL_ATT_000_0 },// Full volume out
{CS4265_DAC_CHB_VOL, CS4265_DAC_VOL_ATT_000_0 },// // Full volume out
{CS4265_DAC_CTL2, CS4265_DAC_CTL2_SOFT_RAMP_ZERO_CROSS_EN },//
{CS4265_SPDIF_CTL1, 0x00 },//
{CS4265_INT_MASK, 0x00 },//
{CS4265_STATUS_MODE_MSB, 0x00 },//
{CS4265_STATUS_MODE_LSB, 0x00 },//
{0xff,0xff}
};
// matching orders
typedef enum { cs4265_ACTIVE = 0, cs4265_STANDBY, cs4265_DOWN, cs4265_ANALOGUE_OFF, cs4265_ANALOGUE_ON, cs4265_VOLUME } dac_cmd_e;
static int cs4265_addr;
static void dac_cmd(dac_cmd_e cmd, ...);
static int cs4265_detect(void);
static uint32_t calc_rnd_mclk_freq(){
float m_scale = (cs4265.i2s_config->sample_rate > 96000 && cs4265.i2s_config->bits_per_sample > 16) ? 4 : 8;
float num_channels = cs4265.i2s_config->channel_format < I2S_CHANNEL_FMT_ONLY_RIGHT ? 2 : 1;
return (uint32_t) round(cs4265.i2s_config->bits_per_sample*i2s_get_clk(cs4265.i2c_port)* m_scale*num_channels/100)*100;
}
static int cs4265_get_clk_index(int mclk, int rate)
{
for (int i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
if (clk_map_table[i].rate == rate &&
clk_map_table[i].mclk == mclk)
return i;
}
return -1;
}
static esp_err_t set_clock(){
esp_err_t err = ESP_OK;
uint32_t mclk = calc_rnd_mclk_freq();
int index = cs4265_get_clk_index(mclk,cs4265.i2s_config->sample_rate );
if (index >= 0) {
ESP_LOGD(TAG, "Setting clock for mclk %u, rate %u (fm mode:%u, clk div:%u))", mclk,cs4265.i2s_config->sample_rate,clk_map_table[index].fm_mode,clk_map_table[index].mclkdiv);
err=cs4265_update_bit(CS4265_ADC_CTL,CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
err|=cs4265_update_bit( CS4265_MCLK_FREQ,CS4265_MCLK_FREQ_MASK,clk_map_table[index].mclkdiv << 4);
} else {
ESP_LOGE(TAG,"can't get correct mclk for ");
return -1;
}
return err;
}
static void get_status(){
uint8_t sts1= adac_read_byte(cs4265_addr, CS4265_INT_STATUS);
ESP_LOGD(TAG,"Status: %s",sts1&CS4265_INT_STATUS_CLKERR?"CLK Error":"CLK OK");
}
/****************************************************************************************
* init
*/
static bool init(char *config, int i2c_port, i2s_config_t *i2s_config) {
// find which TAS we are using (if any)
cs4265_addr = adac_init(config, i2c_port);
cs4265.i2s_config = i2s_config;
cs4265.i2c_port=i2c_port;
if (!cs4265_addr) cs4265_addr = cs4265_detect();
if (!cs4265_addr) {
ESP_LOGE(TAG, "No cs4265 detected");
adac_deinit();
return false;
}
#if BYTES_PER_FRAME == 8
ESP_LOGE(TAG,"The CS4265 does not support 32 bits mode. ");
adac_deinit();
return false;
#endif
// configure MLK
ESP_LOGD(TAG, "Configuring MCLK on GPIO0");
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1);
REG_WRITE(PIN_CTRL, 0xFFFFFFF0);
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
for (int i = 0; cs4265_init_sequence[i].reg != 0xff; i++) {
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (cs4265_addr << 1) | I2C_MASTER_WRITE, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].reg, I2C_MASTER_NACK);
i2c_master_write_byte(i2c_cmd, cs4265_init_sequence[i].value, I2C_MASTER_NACK);
ESP_LOGD(TAG, "i2c write %x at %u", cs4265_init_sequence[i].reg, cs4265_init_sequence[i].value);
}
i2c_master_stop(i2c_cmd);
esp_err_t res = i2c_master_cmd_begin(i2c_port, i2c_cmd, 500 / portTICK_RATE_MS);
i2c_cmd_link_delete(i2c_cmd);
if (res != ESP_OK) {
ESP_LOGE(TAG, "could not intialize cs4265 %d", res);
return false;
}
return true;
}
static esp_err_t cs4265_update_bit(uint8_t reg_no,uint8_t mask,uint8_t val ){
esp_err_t ret=ESP_OK;
uint8_t old= adac_read_byte(cs4265_addr, reg_no);
uint8_t newval = (old & ~mask) | (val & mask);
bool change = old != newval;
if (change){
ret = adac_write_byte(cs4265_addr, reg_no, newval);
if(ret != ESP_OK){
ESP_LOGE(TAG,"Unable to change dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask 0x%02x ",reg_no,old,newval,val,mask);
}
else {
ESP_LOGD(TAG,"Changed dac register 0x%02x [0x%02x->0x%02x] from value 0x%02x, mask 0x%02x ",reg_no,old,newval,val,mask);
}
}
return ret;
}
/****************************************************************************************
* change volume
*/
static bool volume(unsigned left, unsigned right) {
return false;
}
/****************************************************************************************
* power
*/
static void power(adac_power_e mode) {
switch(mode) {
case ADAC_STANDBY:
dac_cmd(cs4265_STANDBY);
break;
case ADAC_ON:
dac_cmd(cs4265_ACTIVE);
break;
case ADAC_OFF:
dac_cmd(cs4265_DOWN);
break;
default:
ESP_LOGW(TAG, "unknown DAC command");
break;
}
}
/****************************************************************************************
* speaker
*/
static void speaker(bool active) {
if (active) dac_cmd(cs4265_ANALOGUE_ON);
else dac_cmd(cs4265_ANALOGUE_OFF);
}
/****************************************************************************************
* headset
*/
static void headset(bool active) { }
/****************************************************************************************
* DAC specific commands
*/
void dac_cmd(dac_cmd_e cmd, ...) {
va_list args;
esp_err_t ret = ESP_OK;
va_start(args, cmd);
switch(cmd) {
case cs4265_VOLUME:
ESP_LOGE(TAG, "DAC volume not handled yet");
break;
case cs4265_ACTIVE:
ESP_LOGD(TAG, "Activating DAC");
adac_write_byte(cs4265_addr, CS4265_PWRCTL,0);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,0);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,0);
cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,0);
break;
case cs4265_STANDBY:
ESP_LOGD(TAG, "DAC Stand-by");
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,CS4265_SPDIF_CTL2_TXOFF);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,CS4265_SPDIF_CTL2_TXMUTE);
cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,CS4265_DAC_CTL_MUTE);
break;
case cs4265_DOWN:
ESP_LOGD(TAG, "DAC Power Down");
adac_write_byte(cs4265_addr, CS4265_PWRCTL,CS4265_PWRCTL_PDN_ALL);
break;
case cs4265_ANALOGUE_OFF:
ESP_LOGD(TAG, "DAC Analog off");
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,CS4265_SPDIF_CTL2_TXOFF);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,CS4265_SPDIF_CTL2_TXMUTE);
cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,CS4265_DAC_CTL_MUTE);
break;
case cs4265_ANALOGUE_ON:
ESP_LOGD(TAG, "DAC Analog on");
adac_write_byte(cs4265_addr, CS4265_PWRCTL,0);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXOFF,0);
cs4265_update_bit(CS4265_SPDIF_CTL2,CS4265_SPDIF_CTL2_TXMUTE,0);
cs4265_update_bit(CS4265_DAC_CTL,CS4265_DAC_CTL_MUTE,0);
break;
}
if (ret != ESP_OK) {
ESP_LOGE(TAG, "could not use cs4265 %d", ret);
}
get_status();
// now set the clock
ret=set_clock(cs4265.i2s_config,cs4265.i2c_port);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "could not set the cs4265's clock %d", ret);
}
va_end(args);
}
/****************************************************************************************
* TAS57 detection
*/
static int cs4265_detect(void) {
uint8_t addr[] = {CS4265_PULL_DOWN,CS4265_PULL_UP};
for (int i = 0; i < sizeof(addr); i++) {
ESP_LOGI(TAG,"Looking for CS4265 @0x%x",addr[i]);
uint8_t reg=adac_read_byte(addr[i], CS4265_CHIP_ID);
if(reg==255){
continue;
}
// found a device at that address
uint8_t devid = reg & CS4265_CHIP_ID_MASK;
if (devid != CS4265_CHIP_ID_VAL) {
ESP_LOGE(TAG,"CS4265 Device ID (%X). Expected %X",devid, CS4265_CHIP_ID);
return 0;
}
ESP_LOGI(TAG,"Found DAC @0x%x, Version %x",addr[i], reg & CS4265_REV_ID_MASK);
return addr[i];
}
return 0;
}

View File

@@ -247,13 +247,17 @@ void decode_close(void) {
#endif
}
void decode_flush(void) {
void decode_flush(bool close) {
LOG_INFO("decode flush");
LOCK_D;
decode.state = DECODE_STOPPED;
IF_PROCESS(
process_flush();
);
if (close && codec) {
codec->close();
codec = NULL;
}
UNLOCK_D;
}

View File

@@ -123,7 +123,7 @@ static bool bt_sink_cmd_handler(bt_sink_cmd_t cmd, va_list args)
{
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_BT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player");
LOG_WARN("Cannot use BT sink while LMS/AirPlay/CSpot are controlling player %d", output.external);
return false;
}
@@ -205,7 +205,7 @@ static bool raop_sink_cmd_handler(raop_event_t event, va_list args)
{
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_RAOP && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player");
LOG_WARN("Cannot use Airplay sink while LMS/BT/CSpot are controlling player %d", output.external);
return false;
}
@@ -338,7 +338,7 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
{
// don't LOCK_O as there is always a chance that LMS takes control later anyway
if (output.external != DECODE_CSPOT && output.state > OUTPUT_STOPPED) {
LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player");
LOG_WARN("Cannot use CSpot sink while LMS/BT/Airplay are controlling player %d", output.external);
return false;
}
@@ -406,9 +406,7 @@ static bool cspot_cmd_handler(cspot_event_t cmd, va_list args)
case CSPOT_VOLUME: {
u32_t volume = va_arg(args, u32_t);
LOG_INFO("CSpot volume %u", volume);
//volume = 65536 * powf(volume / 32768.0f, 3);
// TODO spotify seems to volume normalize crazy high
volume = 4096 * powf(volume / 32768.0f, 3);
volume = 65536 * powf(volume / 65536.0f, 2);
set_volume(volume, volume);
break;
default:

View File

@@ -17,6 +17,7 @@
#include "esp_wifi.h"
#include "monitor.h"
#include "platform_config.h"
#include "messaging.h"
mutex_type slimp_mutex;
static jmp_buf jumpbuf;
@@ -29,6 +30,15 @@ _sig_func_ptr signal(int sig, _sig_func_ptr func) {
return NULL;
}
void em_logprint(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
vmessaging_post_message(MESSAGING_ERROR, MESSAGING_CLASS_SYSTEM, fmt, args);
va_end(args);
fflush(stderr);
}
void *audio_calloc(size_t nmemb, size_t size) {
return calloc(nmemb, size);
}
@@ -55,6 +65,7 @@ int embedded_init(void) {
mutex_create(slimp_mutex);
sb_controls_init();
custom_player_id = sb_displayer_init() ? 100 : 101;
return setjmp(jumpbuf);
}

View File

@@ -51,6 +51,10 @@ extern u8_t custom_player_id;
// to force some special buffer attribute
#define EXT_BSS __attribute__((section(".ext_ram.bss")))
// otherwise just leave it empty
void em_logprint(const char *fmt, ...);
#define LOG_ERROR(fmt, ...) em_logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__);
// all exit() calls are made from main thread (or a function called in main thread)
void embedded_exit(int code);
#define exit(code) do { embedded_exit(code); } while (0)

View File

@@ -275,7 +275,11 @@ static void sighandler(int signum) {
signal(signum, SIG_DFL);
}
#ifndef EMBEDDED
int main(int argc, char **argv) {
#else
int squeezelite_main(int argc, char **argv) {
#endif
char *server = NULL;
char *output_device = "default";
char *include_codecs = NULL;
@@ -382,7 +386,7 @@ int main(int argc, char **argv) {
optarg = NULL;
optind += 1;
} else {
fprintf(stderr, "\nOption error: -%s\n\n", opt);
LOG_ERROR("=> Option error: -%s", opt);
usage(argv[0]);
exit(1);
}
@@ -433,7 +437,7 @@ int main(int argc, char **argv) {
if (!strcmp(l, "all") || !strcmp(l, "ir")) log_ir = new;
#endif
} else {
fprintf(stderr, "\nDebug settings error: -d %s\n\n", optarg);
LOG_ERROR("=> Debug settings error: -d %s", optarg);
usage(argv[0]);
exit(1);
}
@@ -447,7 +451,7 @@ int main(int argc, char **argv) {
int byte = 0;
char *tmp;
if (!strncmp(optarg, "00:04:20", 8)) {
LOG_ERROR("ignoring mac address from hardware player range 00:04:20:**:**:**");
LOG_ERROR("=> ignoring mac address from hardware player range 00:04:20:**:**:**");
} else {
char *t = strtok(optarg, ":");
while (t && byte < 6) {
@@ -685,14 +689,14 @@ int main(int argc, char **argv) {
exit(0);
break;
default:
fprintf(stderr, "Arg error: %s\n", argv[optind]);
LOG_ERROR("=> arg error: %s", argv[optind]);
break;
}
}
// warn if command line includes something which isn't parsed
if (optind < argc) {
fprintf(stderr, "\nError: command line argument error\n\n");
LOG_ERROR("=> command line argument error");
usage(argv[0]);
exit(1);
}
@@ -802,7 +806,7 @@ int main(int argc, char **argv) {
#endif
if (name && namefile) {
fprintf(stderr, "-n and -N option should not be used at same time\n");
LOG_ERROR("=> -n and -N option should not be used at same time");
exit(1);
}
@@ -845,5 +849,5 @@ int main(int argc, char **argv) {
free_ssl_symbols();
#endif
exit(0);
return(0);
}

View File

@@ -30,9 +30,6 @@
* thread has a higher priority. Using an interim buffer where opus decoder writes the output is not great from
* an efficiency (one extra memory copy) point of view, but it allows the lock to not be kept for too long
*/
#if EMBEDDED
#define FRAME_BUF 2048
#endif
#if BYTES_PER_FRAME == 4
#define ALIGN(n) (n)
@@ -40,23 +37,53 @@
#define ALIGN(n) (n << 16)
#endif
#include <opusfile.h>
#include <ogg/ogg.h>
#include <opus.h>
// opus maximum output frames is 120ms @ 48kHz
#define MAX_OPUS_FRAMES 5760
struct opus {
struct OggOpusFile *of;
bool end;
#if FRAME_BUF
u8_t *write_buf;
#endif
#if !LINKALL
// opus symbols to be dynamically loaded
void (*op_free)(OggOpusFile *_of);
int (*op_read)(OggOpusFile *_of, opus_int16 *_pcm, int _buf_size, int *_li);
const OpusHead* (*op_head)(OggOpusFile *_of, int _li);
OggOpusFile* (*op_open_callbacks) (void *_source, OpusFileCallbacks *_cb, unsigned char *_initial_data, size_t _initial_bytes, int *_error);
#endif
enum {OGG_SYNC, OGG_ID_HEADER, OGG_COMMENT_HEADER} status;
ogg_stream_state state;
ogg_packet packet;
ogg_sync_state sync;
ogg_page page;
OpusDecoder* decoder;
int rate, gain, pre_skip;
size_t overframes;
u8_t *overbuf;
int channels;
bool eos;
};
#if !LINKALL
static struct {
void *handle;
int (*ogg_stream_init)(ogg_stream_state* os, int serialno);
int (*ogg_stream_clear)(ogg_stream_state* os);
int (*ogg_stream_reset)(ogg_stream_state* os);
int (*ogg_stream_eos)(ogg_stream_state* os);
int (*ogg_stream_reset_serialno)(ogg_stream_state* os, int serialno);
int (*ogg_sync_clear)(ogg_sync_state* oy);
void (*ogg_packet_clear)(ogg_packet* op);
char* (*ogg_sync_buffer)(ogg_sync_state* oy, long size);
int (*ogg_sync_wrote)(ogg_sync_state* oy, long bytes);
long (*ogg_sync_pageseek)(ogg_sync_state* oy, ogg_page* og);
int (*ogg_sync_pageout)(ogg_sync_state* oy, ogg_page* og);
int (*ogg_stream_pagein)(ogg_stream_state* os, ogg_page* og);
int (*ogg_stream_packetout)(ogg_stream_state* os, ogg_packet* op);
int (*ogg_page_packets)(const ogg_page* og);
} go;
static struct {
void* handle;
OpusDecoder* (*opus_decoder_create)(opus_int32 Fs, int channels, int* error);
int (*opus_decode)(OpusDecoder* st, const unsigned char* data, opus_int32 len, opus_int16* pcm, int frame_size, int decode_fec);
void (*opus_decoder_destroy)(OpusDecoder* st);
} gu;
#endif
static struct opus *u;
extern log_level loglevel;
@@ -75,72 +102,136 @@ extern struct processstate process;
#if PROCESS
#define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex)
#define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex)
#define LOCK_O_not_direct if (!decode.direct) mutex_lock(outputbuf->mutex)
#define UNLOCK_O_not_direct if (!decode.direct) mutex_unlock(outputbuf->mutex)
#define IF_DIRECT(x) if (decode.direct) { x }
#define IF_PROCESS(x) if (!decode.direct) { x }
#else
#define LOCK_O_direct mutex_lock(outputbuf->mutex)
#define UNLOCK_O_direct mutex_unlock(outputbuf->mutex)
#define LOCK_O_not_direct
#define UNLOCK_O_not_direct
#define IF_DIRECT(x) { x }
#define IF_PROCESS(x)
#endif
#if LINKALL
#define OP(h, fn, ...) (op_ ## fn)(__VA_ARGS__)
#define OG(h, fn, ...) (ogg_ ## fn)(__VA_ARGS__)
#define OP(h, fn, ...) (opus_ ## fn)(__VA_ARGS__)
#else
#define OP(h, fn, ...) (h)->op_ ## fn(__VA_ARGS__)
#define OG(h, fn, ...) (h)->ogg_ ## fn(__VA_ARGS__)
#define OP(h, fn, ...) (h)->opus_ ## fn(__VA_ARGS__)
#endif
// called with mutex locked within vorbis_decode to avoid locking O before S
static int _read_cb(void *datasource, char *ptr, int size) {
size_t bytes;
static unsigned parse_uint16(const unsigned char* _data) {
return _data[0] | _data[1] << 8;
}
static int parse_int16(const unsigned char* _data) {
return ((_data[0] | _data[1] << 8) ^ 0x8000) - 0x8000;
}
static opus_uint32 parse_uint32(const unsigned char* _data) {
return _data[0] | (opus_uint32)_data[1] << 8 |
(opus_uint32)_data[2] << 16 | (opus_uint32)_data[3] << 24;
}
static int get_opus_packet(void) {
int status = 0;
LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (!(status = OG(&go, stream_packetout, &u->state, &u->packet)) && bytes) {
do {
size_t consumed = min(bytes, 4096);
char* buffer = OG(&gu, sync_buffer, &u->sync, consumed);
memcpy(buffer, streambuf->readp, consumed);
OG(&gu, sync_wrote, &u->sync, consumed);
bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
bytes = min(bytes, size);
_buf_inc_readp(streambuf, consumed);
bytes -= consumed;
} while (!(status = OG(&gu, sync_pageseek, &u->sync, &u->page)) && bytes);
memcpy(ptr, streambuf->readp, bytes);
_buf_inc_readp(streambuf, bytes);
// if we have a new page, put it in
if (status) OG(&go, stream_pagein, &u->state, &u->page);
}
UNLOCK_S;
return status;
}
return bytes;
static int read_opus_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) {
size_t consumed = min(bytes, 4096);
char* buffer = OG(&gu, sync_buffer, &u->sync, consumed);
memcpy(buffer, streambuf->readp, consumed);
OG(&gu, sync_wrote, &u->sync, consumed);
_buf_inc_readp(streambuf, consumed);
bytes -= consumed;
if (!OG(&gu, sync_pageseek, &u->sync, &u->page)) continue;
}
switch (u->status) {
case OGG_SYNC:
u->status = OGG_ID_HEADER;
OG(&gu, stream_reset_serialno, &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;
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);
if (!u->decoder || status != OPUS_OK) {
LOG_ERROR("can't create decoder %d (channels:%u)", status, u->channels);
}
}
fetch = true;
break;
case OGG_COMMENT_HEADER:
// skip pakets to consume VorbisComment. With opus, header packets align on pages
status = OG(&gu, page_packets, &u->page);
break;
default:
break;
}
}
UNLOCK_S;
return status;
}
static decode_state opus_decompress(void) {
frames_t frames;
int n;
static int channels;
u8_t *write_buf;
LOCK_S;
if (decode.new_stream) {
int status = read_opus_header();
if (stream.state <= DISCONNECT && u->end) {
UNLOCK_S;
return DECODE_COMPLETE;
}
UNLOCK_S;
if (decode.new_stream) {
struct OpusFileCallbacks cbs;
const struct OpusHead *info;
int err;
cbs.read = (op_read_func) _read_cb;
cbs.seek = NULL; cbs.tell = NULL; cbs.close = NULL;
if ((u->of = OP(u, open_callbacks, streambuf, &cbs, NULL, 0, &err)) == NULL) {
LOG_WARN("open_callbacks error: %d", err);
return DECODE_COMPLETE;
if (status == 0) {
return DECODE_RUNNING;
} else if (status < 0) {
LOG_WARN("can't create codec");
return DECODE_ERROR;
}
info = OP(u, head, u->of, -1);
LOCK_O;
output.next_sample_rate = decode_newstream(48000, output.supported_rates);
IF_DSD( output.next_fmt = PCM; )
@@ -148,46 +239,59 @@ static decode_state opus_decompress(void) {
if (output.fade_mode) _checkfade(true);
decode.new_stream = false;
UNLOCK_O;
channels = info->channel_count;
if (u->channels > 2) {
LOG_WARN("too many channels: %d", u->channels);
return DECODE_ERROR;
}
LOG_INFO("setting track_start");
}
#if FRAME_BUF
IF_DIRECT(
frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;
frames = min(frames, FRAME_BUF);
write_buf = u->write_buf;
);
#else
LOCK_O_direct;
IF_DIRECT(
frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;
write_buf = outputbuf->writep;
);
#endif
IF_PROCESS(
frames = process.max_in_frames;
write_buf = process.inbuf;
);
u->end = frames == 0;
// write the decoded frames into outputbuf then unpack them (they are 16 bits)
n = OP(u, read, u->of, (opus_int16*) write_buf, frames * channels, NULL);
// get some packets and decode them, or use the leftover from previous pass
if (u->overframes) {
/* use potential leftover from previous encoding. We know that it will fit this time
* as min_space is >=MAX_OPUS_FRAMES and we start from the beginning of the buffer */
memcpy(write_buf, u->overbuf, u->overframes * BYTES_PER_FRAME);
n = u->overframes;
u->overframes = 0;
} else if (get_opus_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);
if (n > 0) {
u->overframes = n - min(n, frames);
n = min(n, frames);
memcpy(write_buf, u->overbuf, n * BYTES_PER_FRAME);
memmove(u->overbuf, u->overbuf + n, u->overframes);
}
} else {
/* we just do one packet at a time, although we could loop on packets but that means locking the
* 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 (!OG(&go, page_eos, &u->page)) {
UNLOCK_O_direct;
return DECODE_RUNNING;
} else u->eos = true;
#if FRAME_BUF
LOCK_O_direct;
#endif
if (n > 0) {
frames_t count;
s16_t *iptr;
ISAMPLE_T *optr;
frames = n;
count = frames * channels;
count = frames * u->channels;
// work backward to unpack samples (if needed)
iptr = (s16_t *) write_buf + count;
@@ -198,20 +302,13 @@ static decode_state opus_decompress(void) {
optr = (ISAMPLE_T *) write_buf + frames * 2;
)
if (channels == 2) {
#if BYTES_PER_FRAME == 4
#if FRAME_BUF
// copy needed only when DIRECT and FRAME_BUF
IF_DIRECT(
memcpy(outputbuf->writep, write_buf, frames * BYTES_PER_FRAME);
)
#endif
#else
if (u->channels == 2) {
#if BYTES_PER_FRAME == 8
while (count--) {
*--optr = ALIGN(*--iptr);
}
#endif
} else if (channels == 1) {
} else if (u->channels == 1) {
while (count--) {
*--optr = ALIGN(*--iptr);
*--optr = ALIGN(*iptr);
@@ -229,69 +326,85 @@ static decode_state opus_decompress(void) {
} else if (n == 0) {
if (stream.state <= DISCONNECT) {
LOG_INFO("partial decode");
if (stream.state <= DISCONNECT && u->eos) {
LOG_INFO("end of decode");
UNLOCK_O_direct;
return DECODE_COMPLETE;
} else {
LOG_INFO("no frame decoded");
}
} else if (n == OP_HOLE) {
// recoverable hole in stream, seen when skipping
LOG_DEBUG("hole in stream");
} else {
LOG_INFO("op_read error: %d", n);
LOG_INFO("opus decode error: %d", n);
UNLOCK_O_direct;
return DECODE_COMPLETE;
}
UNLOCK_O_direct;
return DECODE_RUNNING;
}
static void opus_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
if (!u->of) {
#if FRAME_BUF
if (!u->write_buf) u->write_buf = malloc(FRAME_BUF * BYTES_PER_FRAME);
#endif
} else {
OP(u, free, u->of);
u->of = NULL;
}
u->end = false;
static void opus_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
if (u->decoder) OP(&gu, decoder_destroy, u->decoder);
u->decoder = NULL;
if (!u->overbuf) u->overbuf = malloc(MAX_OPUS_FRAMES * BYTES_PER_FRAME);
u->eos = false;
u->status = OGG_SYNC;
u->overframes = 0;
OG(&gu, sync_clear, &u->sync);
OG(&gu, stream_clear, &u->state);
OG(&gu, stream_init, &u->state, -1);
}
static void opus_close(void) {
if (u->of) {
OP(u, free, u->of);
u->of = NULL;
}
#if FRAME_BUF
free(u->write_buf);
u->write_buf = NULL;
#endif
static void opus_close(void) {
if (u->decoder) OP(&gu, decoder_destroy, u->decoder);
u->decoder = NULL;
free(u->overbuf);
u->overbuf = NULL;
OG(&gu, stream_clear, &u->state);
OG(&gu, sync_clear, &u->sync);
}
static bool load_opus(void) {
#if !LINKALL
void *handle = dlopen(LIBOPUS, RTLD_NOW);
char *err;
if (!handle) {
LOG_INFO("dlerror: %s", dlerror());
void *u.handle = dlopen(LIBOPUS, RTLD_NOW);
if (!u_handle) {
LOG_INFO("opus dlerror: %s", dlerror());
return false;
}
u->op_free = dlsym(handle, "op_free");
u->op_read = dlsym(handle, "op_read");
u->op_head = dlsym(handle, "op_head");
u->op_open_callbacks = dlsym(handle, "op_open_callbacks");
void *g_handle = dlopen(LIBOGG, RTLD_NOW);
if (!g_handle) {
dlclose(u_handle);
LOG_INFO("ogg dlerror: %s", dlerror());
return false;
}
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_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");
g_handle->ogg_packet_clear = dlsym(g_handle->handle, "ogg_packet_clear");
g_handle->ogg_sync_buffer = dlsym(g_handle->handle, "ogg_sync_buffer");
g_handle->ogg_sync_wrote = dlsym(g_handle->handle, "ogg_sync_wrote");
g_handle->ogg_sync_pageseek = dlsym(g_handle->handle, "ogg_sync_pageseek");
g_handle->ogg_sync_pageout = dlsym(g_handle->handle, "ogg_sync_pageout");
g_handle->ogg_stream_pagein = dlsym(g_handle->handle, "ogg_stream_pagein");
g_handle->ogg_stream_packetout = dlsym(g_handle->handle, "ogg_stream_packetout");
g_handle->ogg_page_packets = dlsym(g_handle->handle, "ogg_page_packets");
u_handle->opus_decoder_create = dlsym(u_handle->handle, "opus_decoder_create");
u_handle->opus_decoder_destroy = dlsym(u_handle->handle, "opus_decoder_destroy");
u_handle->opus_decode = dlsym(u_handle->handle, "opus_decode");
if ((err = dlerror()) != NULL) {
LOG_INFO("dlerror: %s", err);
@@ -308,23 +421,17 @@ struct codec *register_opus(void) {
static struct codec ret = {
'u', // id
"ops", // types
4*1024, // min read
32*1024, // min space
8*1024, // min read
MAX_OPUS_FRAMES*BYTES_PER_FRAME*2, // min space
opus_open, // open
opus_close, // close
opus_decompress, // decode
};
u = malloc(sizeof(struct opus));
if (!u) {
if ((u = calloc(1, sizeof(struct opus))) == NULL) {
return NULL;
}
u->of = NULL;
#if FRAME_BUF
u->write_buf = NULL;
#endif
if (!load_opus()) {
return NULL;
}

View File

@@ -306,7 +306,7 @@ static void process_strm(u8_t *pkt, int len) {
break;
case 'f':
case 'q':
decode_flush();
decode_flush(strm->command == 'q');
if (!output.external) output_flush();
status.frames_played = 0;
if (stream_disconnect() && strm->command == 'f') sendSTAT("STMf", 0);

View File

@@ -393,7 +393,6 @@ typedef enum { lERROR = 0, lWARN, lINFO, lDEBUG, lSDEBUG } log_level;
const char *logtime(void);
void logprint(const char *fmt, ...);
#define LOG_ERROR(fmt, ...) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define LOG_WARN(fmt, ...) if (loglevel >= lWARN) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) if (loglevel >= lINFO) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define LOG_DEBUG(fmt, ...) if (loglevel >= lDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
@@ -406,6 +405,10 @@ typedef int sockfd;
#include "embedded.h"
#endif
#ifndef LOG_ERROR
#define LOG_ERROR(fmt, ...) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
#if !defined(MSG_NOSIGNAL)
#define MSG_NOSIGNAL 0
#endif
@@ -619,7 +622,7 @@ struct codec {
void decode_init(log_level level, const char *include_codecs, const char *exclude_codecs);
void decode_close(void);
void decode_flush(void);
void decode_flush(bool close);
unsigned decode_newstream(unsigned sample_rate, unsigned supported_rates[]);
void codec_open(u8_t format, u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness);

View File

@@ -390,7 +390,7 @@ void stream_init(log_level level, unsigned stream_buf_size) {
buf_init(streambuf, stream_buf_size);
if (streambuf->buf == NULL) {
LOG_ERROR("unable to malloc buffer");
exit(0);
exit(2);
}
#if USE_SSL
@@ -401,7 +401,7 @@ void stream_init(log_level level, unsigned stream_buf_size) {
SSLctx = SSL_CTX_new(SSLv23_client_method());
if (SSLctx == NULL) {
LOG_ERROR("unable to allocate SSL context");
exit(0);
exit(3);
}
SSL_CTX_set_options(SSLctx, SSL_OP_NO_SSLv2);
#if !LINKALL && !NO_SSLSYM

View File

@@ -69,7 +69,11 @@ const char *logtime(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
strftime(buf, sizeof(buf), "[%T.", localtime(&tv.tv_sec));
sprintf(buf+strlen(buf), "%06ld]", (long)tv.tv_usec);
#ifdef EMBEDDED
sprintf(buf+strlen(buf), "%03ld]", (long)tv.tv_usec/1000);
#else
sprintf(buf+strlen(buf), "%06ld]", (long)tv.tv_usec);
#endif
#endif
return buf;
}

View File

@@ -21,52 +21,81 @@
#include "squeezelite.h"
/*
* with some low-end CPU, the decode call takes a fair bit of time and if the outputbuf is locked during that
* period, the output_thread (or equivalent) will be locked although there is plenty of samples available.
* Normally, with PRIO_INHERIT, that thread should increase decoder priority and get the lock quickly but it
* seems that when the streambuf has plenty of data, the decode thread grabs the CPU to much, even it the output
* thread has a higher priority. Using an interim buffer where vorbis decoder writes the output is not great from
* an efficiency (one extra memory copy) point of view, but it allows the lock to not be kept for too long
*/
#if EMBEDDED
#define FRAME_BUF 2048
#endif
#if BYTES_PER_FRAME == 4
#define ALIGN(n) (n)
#else
#define ALIGN(n) (n << 16)
#endif
// automatically select between floating point (preferred) and fixed point libraries:
// NOTE: works with Tremor version here: http://svn.xiph.org/trunk/Tremor, not vorbisidec.1.0.2 currently in ubuntu
// we take common definations from <vorbis/vorbisfile.h> even though we can use tremor at run time
// tremor's OggVorbis_File struct is normally smaller so this is ok, but padding added to malloc in case it is bigger
#define OV_EXCLUDE_STATIC_CALLBACKS
#include <ogg/ogg.h>
#ifdef TREMOR_ONLY
#include <ivorbisfile.h>
#include <vorbis/ivorbiscodec.h>
#else
#include <vorbis/vorbisfile.h>
#include <vorbis/codec.h>
static bool tremor = false;
#endif
// this is tremor packing, not mine...
static inline int32_t clip15(int32_t x) {
int ret = x;
ret -= ((x<=32767)-1)&(x-32767);
ret -= ((x>=-32768)-1)&(x+32768);
return ret;
}
#if BYTES_PER_FRAME == 4
#define ALIGN(n) clip15((n) >> 9);
#define ALIGN_FLOAT(n) ((n)*32768.0f + 0.5f)
#else
#define ALIGN(n) (clip15((n) >> 9) << 16)
#define ALIGN_FLOAT ((n)*32768.0f*65536.0f + 0.5f)
#endif
struct vorbis {
OggVorbis_File *vf;
bool opened, end;
#if FRAME_BUF
u8_t *write_buf;
#endif
bool opened;
enum { OGG_SYNC, OGG_ID_HEADER, OGG_COMMENT_HEADER, OGG_SETUP_HEADER } status;
struct {
ogg_stream_state state;
ogg_packet packet;
ogg_sync_state sync;
ogg_page page;
};
struct {
vorbis_dsp_state decoder;
vorbis_info info;
vorbis_comment comment;
vorbis_block block;
};
int rate, channels;
uint32_t overflow;
bool eos;
};
#if !LINKALL
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);
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);
} gv;
static struct {
void *handle;
int (*ogg_stream_init)(ogg_stream_state* os, int serialno);
int (*ogg_stream_clear)(ogg_stream_state* os);
int (*ogg_stream_reset)(ogg_stream_state* os);
int (*ogg_stream_eos)(ogg_stream_state* os);
int (*ogg_stream_reset_serialno)(ogg_stream_state* os, int serialno);
int (*ogg_sync_clear)(ogg_sync_state* oy);
void (*ogg_packet_clear)(ogg_packet* op);
char* (*ogg_sync_buffer)(ogg_sync_state* oy, long size);
int (*ogg_sync_wrote)(ogg_sync_state* oy, long bytes);
long (*ogg_sync_pageseek)(ogg_sync_state* oy, ogg_page* og);
int (*ogg_sync_pageout)(ogg_sync_state* oy, ogg_page* og);
int (*ogg_stream_pagein)(ogg_stream_state* os, ogg_page* og);
int (*ogg_stream_packetout)(ogg_stream_state* os, ogg_packet* op);
int (*ogg_page_packets)(const ogg_page* og);
} go;
#endif
};
static struct vorbis *v;
@@ -86,183 +115,249 @@ extern struct processstate process;
#if PROCESS
#define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex)
#define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex)
#define LOCK_O_not_direct if (!decode.direct) mutex_lock(outputbuf->mutex)
#define UNLOCK_O_not_direct if (!decode.direct) mutex_unlock(outputbuf->mutex)
#define IF_DIRECT(x) if (decode.direct) { x }
#define IF_PROCESS(x) if (!decode.direct) { x }
#else
#define LOCK_O_direct mutex_lock(outputbuf->mutex)
#define UNLOCK_O_direct mutex_unlock(outputbuf->mutex)
#define LOCK_O_not_direct
#define UNLOCK_O_not_direct
#define IF_DIRECT(x) { x }
#define IF_PROCESS(x)
#endif
#if LINKALL
#define OV(h, fn, ...) (ov_ ## fn)(__VA_ARGS__)
#define TREMOR(h) 0
#if !WIN
extern int ov_read_tremor(); // needed to enable compilation, not linked
#endif
#define OV(h, fn, ...) (vorbis_ ## fn)(__VA_ARGS__)
#define OG(h, fn, ...) (ogg_ ## fn)(__VA_ARGS__)
#else
#define OV(h, fn, ...) (h)->ov_##fn(__VA_ARGS__)
#define TREMOR(h) (h)->ov_read_tremor
#define OG(h, fn, ...) (h)->ogg_ ## fn(__VA_ARGS__)
#endif
// called with mutex locked within vorbis_decode to avoid locking O before S
static size_t _read_cb(void *ptr, size_t size, size_t nmemb, void *datasource) {
size_t bytes;
static int get_ogg_packet(void) {
int status = 0;
LOCK_S;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
bytes = min(bytes, size * nmemb);
while (!(status = OG(&go, stream_packetout, &v->state, &v->packet)) && bytes) {
do {
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);
memcpy(ptr, streambuf->readp, bytes);
_buf_inc_readp(streambuf, bytes);
_buf_inc_readp(streambuf, consumed);
bytes -= consumed;
} while (!(status = OG(&go, sync_pageseek, &v->sync, &v->page)) && bytes);
// if we have a new page, put it in
if (status) OG(&go, stream_pagein, &v->state, &v->page);
}
UNLOCK_S;
return bytes / size;
return status;
}
// these are needed for older versions of tremor, later versions and libvorbis allow NULL to be used
static int _seek_cb(void *datasource, ogg_int64_t offset, int whence) { return -1; }
static int _close_cb(void *datasource) { return 0; }
static long _tell_cb(void *datasource) { return 0; }
static decode_state vorbis_decode(void) {
static int channels;
frames_t frames;
int bytes, s, n;
u8_t *write_buf;
static int read_vorbis_header(void) {
int status = 0;
bool fetch = true;
LOCK_S;
if (stream.state <= DISCONNECT && v->end) {
UNLOCK_S;
return DECODE_COMPLETE;
size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf));
while (bytes && !status) {
// first fetch a page if we need one
if (fetch) {
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;
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;
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;
} 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");
LOG_INFO("id acquired");
}
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);
// 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);
// no need to free comment, it's fake
OV(&gv, info_clear, &v->info);
status = -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;
}
//@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;
}
inline int pcm_out(vorbis_dsp_state* decoder, void*** pcm) {
#ifndef TREMOR_ONLY
if (!tremor) return OV(&gv, synthesis_pcmout, decoder, (ogg_float_t***) pcm);
#endif
return OV(&gv, synthesis_pcmout, decoder, (ogg_int32_t***) pcm);
}
static decode_state vorbis_decode(void) {
frames_t frames;
int n = 0;
u8_t *write_buf;
void** pcm = NULL;
if (decode.new_stream) {
ov_callbacks cbs;
int err;
struct vorbis_info *info;
int status = read_vorbis_header();
cbs.read_func = _read_cb;
if (TREMOR(v)) {
cbs.seek_func = _seek_cb; cbs.close_func = _close_cb; cbs.tell_func = _tell_cb;
} else {
cbs.seek_func = NULL; cbs.close_func = NULL; cbs.tell_func = NULL;
if (status == 0) {
return DECODE_RUNNING;
} else if (status < 0) {
LOG_WARN("can't create codec");
return DECODE_ERROR;
}
if ((err = OV(v, open_callbacks, streambuf, v->vf, NULL, 0, cbs)) < 0) {
LOG_WARN("open_callbacks error: %d", err);
return DECODE_COMPLETE;
}
v->opened = true;
info = OV(v, info, v->vf, -1);
LOG_INFO("setting track_start");
LOCK_O;
output.next_sample_rate = decode_newstream(info->rate, output.supported_rates);
output.next_sample_rate = decode_newstream(v->rate, output.supported_rates);
IF_DSD( output.next_fmt = PCM; )
output.track_start = outputbuf->writep;
if (output.fade_mode) _checkfade(true);
decode.new_stream = false;
UNLOCK_O;
channels = info->channels;
if (channels > 2) {
LOG_WARN("too many channels: %d", channels);
if (v->channels > 2) {
LOG_WARN("too many channels: %d", v->channels);
return DECODE_ERROR;
}
LOG_INFO("setting track_start");
}
#if FRAME_BUF
IF_DIRECT(
frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;
frames = min(frames, FRAME_BUF);
write_buf = v->write_buf;
);
#else
LOCK_O_direct;
IF_DIRECT(
frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME;
write_buf = outputbuf->writep;
);
#endif
IF_PROCESS(
frames = process.max_in_frames;
write_buf = process.inbuf;
);
bytes = frames * 2 * channels; // samples returned are 16 bits
v->end = frames == 0;
// write the decoded frames into outputbuf even though they are 16 bits per sample, then unpack them
#ifdef TREMOR_ONLY
n = OV(v, read, v->vf, (char *)write_buf, bytes, &s);
#else
if (!TREMOR(v)) {
#if SL_LITTLE_ENDIAN
n = OV(v, read, v->vf, (char *)write_buf, bytes, 0, 2, 1, &s);
#else
n = OV(v, read, v->vf, (char *)write_buf, bytes, 1, 2, 1, &s);
#endif
#if !WIN
} else {
n = OV(v, read_tremor, v->vf, (char *)write_buf, bytes, &s);
#endif
}
#endif
#if FRAME_BUF
LOCK_O_direct;
#endif
if (v->overflow) {
n = pcm_out(&v->decoder, &pcm);
v->overflow = n - min(n, frames);
} else if (get_ogg_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 (!OG(&go, page_eos, &v->page)) {
UNLOCK_O_direct;
return DECODE_RUNNING;
} else v->eos = true;
if (n > 0) {
frames_t count;
s16_t *iptr;
ISAMPLE_T *optr;
ISAMPLE_T *optr = (ISAMPLE_T*) write_buf;
frames = min(n, frames);
frames_t count = frames;
#ifndef TREMOR_ONLY
if (!tremor) {
if (v->channels == 2) {
float* iptr_l = (float*) pcm[0];
float* iptr_r = (float*) pcm[1];
frames = n / 2 / channels;
count = frames * channels;
// work backward to unpack samples (if needed)
iptr = (s16_t *) write_buf + count;
IF_DIRECT(
optr = (ISAMPLE_T *) outputbuf->writep + frames * 2;
)
IF_PROCESS(
optr = (ISAMPLE_T *) write_buf + frames * 2;
)
if (channels == 2) {
#if BYTES_PER_FRAME == 4
#if FRAME_BUF
// copy needed only when DIRECT and FRAME_BUF
IF_DIRECT(
memcpy(outputbuf->writep, write_buf, frames * BYTES_PER_FRAME);
)
#endif
while (count--) {
*optr++ = ALIGN_FLOAT(*iptr_l++);
*optr++ = ALIGN_FLOAT(*iptr_r++);;
}
} else if (v->channels == 1) {
float* iptr = pcm[0];
while (count--) {
*optr++ = ALIGN_FLOAT(*iptr);
*optr++ = ALIGN_FLOAT(*iptr++);
}
}
} else
#else
while (count--) {
*--optr = ALIGN(*--iptr);
}
{
if (v->channels == 2) {
s32_t* iptr_l = (s32_t*) pcm[0];
s32_t* iptr_r = (s32_t*) pcm[1];
while (count--) {
*optr++ = ALIGN(*iptr_l++);
*optr++ = ALIGN(*iptr_r++);
}
} else if (v->channels == 1) {
s32_t* iptr = (s32_t*) pcm[0];
while (count--) {
*optr++ = ALIGN(*iptr);
*optr++ = ALIGN(*iptr++);
}
}
}
#endif
} else if (channels == 1) {
while (count--) {
*--optr = ALIGN(*--iptr);
*--optr = ALIGN(*iptr);
}
}
// return samples to vorbis/tremor decoder
OV(&gv, synthesis_read, &v->decoder, frames);
IF_DIRECT(
_buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME);
@@ -275,19 +370,14 @@ static decode_state vorbis_decode(void) {
} else if (n == 0) {
if (stream.state <= DISCONNECT) {
LOG_INFO("partial decode");
if (stream.state <= DISCONNECT && v->eos) {
LOG_INFO("end of decode");
UNLOCK_O_direct;
return DECODE_COMPLETE;
} else {
LOG_INFO("no frame decoded");
}
} else if (n == OV_HOLE) {
// recoverable hole in stream, seen when skipping
LOG_DEBUG("hole in stream");
} else {
LOG_INFO("ov_read error: %d", n);
@@ -300,55 +390,81 @@ static decode_state vorbis_decode(void) {
}
static void vorbis_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) {
if (!v->vf) {
v->vf = malloc(sizeof(OggVorbis_File) + 128); // add some padding as struct size may be larger
memset(v->vf, 0, sizeof(OggVorbis_File) + 128);
#if FRAME_BUF
v->write_buf = malloc(FRAME_BUF * BYTES_PER_FRAME);
#endif
} else {
if (v->opened) {
OV(v, clear, v->vf);
v->opened = false;
}
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);
}
v->eos = false;
v->opened = false;
v->status = OGG_SYNC;
v->overflow = 0;
OG(&gu, sync_clear, &v->sync);
OG(&gu, stream_clear, &v->state);
OG(&gu, stream_init, &v->state, -1);
}
static void vorbis_close(void) {
static void vorbis_close() {
return;
LOG_INFO("CLOSING CODEC");
if (v->opened) {
OV(v, clear, v->vf);
v->opened = false;
OV(&go, block_clear, &v->block);
OV(&go, info_clear, &v->info);
OV(&go, dsp_clear, &v->decoder);
}
free(v->vf);
#if FRAME_BUF
free(v->write_buf);
v->write_buf = NULL;
#endif
v->vf = NULL;
v->end = false;
v->opened = false;
OG(&go, stream_clear, &v->state);
OG(&go, sync_clear, &v->sync);
}
static bool load_vorbis() {
#if !LINKALL
void *handle = dlopen(LIBVORBIS, RTLD_NOW);
char *err;
bool tremor = false;
if (!handle) {
handle = dlopen(LIBTREMOR, RTLD_NOW);
if (handle) {
char *err;
void *g_handle = dlopen(LIBOGG, RTLD_NOW);
if (!g_handle) {
LOG_INFO("ogg dlerror: %s", dlerror());
return false
}
void *v_handle = NULL;
#ifndef TREMOR_ONLY
v_handle = dlopen(LIBVORBIS, RTLD_NOW);
#endif
if (!v_handle) {
v_handle = dlopen(LIBTREMOR, RTLD_NOW);
if (v_handle) {
tremor = true;
} else {
LOG_INFO("dlerror: %s", dlerror());
dlclose(g_handle);
LOG_INFO("vorbis/tremor dlerror: %s", dlerror());
return false;
}
}
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_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");
g_handle->ogg_packet_clear = dlsym(g_handle->handle, "ogg_packet_clear");
g_handle->ogg_sync_buffer = dlsym(g_handle->handle, "ogg_sync_buffer");
g_handle->ogg_sync_wrote = dlsym(g_handle->handle, "ogg_sync_wrote");
g_handle->ogg_sync_pageseek = dlsym(g_handle->handle, "ogg_sync_pageseek");
g_handle->ogg_sync_pageout = dlsym(g_handle->handle, "ogg_sync_pageout");
g_handle->ogg_stream_pagein = dlsym(g_handle->handle, "ogg_stream_pagein");
g_handle->ogg_stream_packetout = dlsym(g_handle->handle, "ogg_stream_packetout");
g_handle->ogg_page_packets = dlsym(g_handle->handle, "ogg_page_packets");
v->ov_read = tremor ? NULL : dlsym(handle, "ov_read");
v->ov_read_tremor = tremor ? dlsym(handle, "ov_read") : NULL;
v->ov_info = dlsym(handle, "ov_info");
v->ov_clear = dlsym(handle, "ov_clear");
v->ov_open_callbacks = dlsym(handle, "ov_open_callbacks");
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_open_callbacks = dlsym(handle, "ov_open_callbacks");
if ((err = dlerror()) != NULL) {
LOG_INFO("dlerror: %s", err);
@@ -372,12 +488,10 @@ struct codec *register_vorbis(void) {
vorbis_decode,// decode
};
v = malloc(sizeof(struct vorbis));
if (!v) {
if ((v = calloc(1, sizeof(struct vorbis))) == NULL) {
return NULL;
}
v->vf = NULL;
v->opened = false;
if (!load_vorbis()) {

View File

@@ -128,8 +128,8 @@ void init_telnet(){
if (bMirrorToUART) uart_fd = open("/dev/uart/0", O_RDWR);
ESP_ERROR_CHECK(esp_vfs_register("/dev/pkspstdout", &vfs, NULL));
freopen("/dev/pkspstdout", "wb", stdout);
freopen("/dev/pkspstdout", "wb", stderr);
freopen("/dev/pkspstdout", "w", stdout);
freopen("/dev/pkspstdout", "w", stderr);
bIsEnabled=true;
}

View File

@@ -266,6 +266,11 @@ cJSON* network_status_get_basic_info(cJSON** old) {
*old = network_status_update_float(old, "avg_conn_time", nm->num_disconnect > 0 ? (nm->total_connected_time / nm->num_disconnect) : 0);
*old = network_update_cjson_number(old, "bt_status", bt_app_source_get_a2d_state());
*old = network_update_cjson_number(old, "bt_sub_status", bt_app_source_get_media_state());
#if DEPTH == 16
*old = network_update_cjson_number(old, "depth", 16);
#elif DEPTH == 32
*old = network_update_cjson_number(old, "depth", 32);
#endif
#if CONFIG_I2C_LOCKED
*old = network_status_update_bool(old, "is_i2c_locked", true);
#else

File diff suppressed because one or more lines are too long

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

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -24,6 +24,23 @@ 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 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;
@@ -50,6 +67,40 @@ 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;
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 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 function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;
declare function pushStatus(): void;

View File

@@ -1,3 +1,4 @@
declare const PORT: 9100;
import HtmlWebPackPlugin = require("html-webpack-plugin");
export namespace entry {
const test: string;
@@ -17,7 +18,7 @@ export namespace devServer {
}
export const open: boolean;
export const compress: boolean;
export const port: number;
export { PORT as port };
export const host: string;
export const allowedHosts: string;
export const headers: {
@@ -33,3 +34,4 @@ export namespace devServer {
export function onBeforeSetupMiddleware(devServer: any): void;
}
export const plugins: HtmlWebPackPlugin[];
export {};

View File

@@ -23,5 +23,6 @@
"mock_plugin_has_proxy": "x",
"mock_fail_fw_update":"",
"mock_fail_recovery":"",
"mock_old_recovery":""
"mock_old_recovery":"",
"depth": 16
}

View File

@@ -8,6 +8,7 @@
"scripts": {
"prod": "webpack serve --open --mode=production",
"build": "webpack --mode=production ",
"stats": "webpack --env ANALYZE_SIZE=1 --mode=production ",
"watch": "webpack --progress --watch --mode=development ",
"dev": "webpack serve --open --mode=development"
},
@@ -51,6 +52,7 @@
"lodash-webpack-plugin": "^0.11.6",
"mini-css-extract-plugin": "^2.5.2",
"node-sass": "^7.0.1",
"open": "^9.1.0",
"postcss": "^8.4.5",
"postcss-loader": "^6.2.1",
"purgecss-webpack-plugin": "^4.1.3",
@@ -65,7 +67,7 @@
"ts-loader": "^9.2.6",
"typescript": "^4.5.5",
"webpack": "^5.67.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.3"
},
@@ -74,7 +76,9 @@
"async-mutex": "^0.3.2",
"bootstrap": "^5.1.3",
"jquery": "^3.6.0",
"popper.js": "^1.16.1"
"popper.js": "^1.16.1",
"webpack-visualizer-plugin": "^0.1.11",
"webpack-visualizer-plugin2": "^1.0.0"
},
"keywords": [
"webppack4",

View File

@@ -44,19 +44,23 @@
</div>
<div class="info navbar-right" style="display: inline-flex;">
<span class="recovery_element material-icons " style="color:orange; display: none" aria-label="🛑" >system_update_alt</span>
<span id="battery" class="material-icons" style="fill:white; display: none" aria-label="🔋" >battery_full</span>
<span class="recovery_element material-icons " style="color:orange; display: none"
aria-label="🛑">system_update_alt</span>
<span id="battery" class="material-icons" style="fill:white; display: none"
aria-label="🔋">battery_full</span>
<span id="o_jack" class="material-icons" style="fill:white; display: none" aria-label="🎧">headphones</span>
<span id="s_airplay" class="material-icons" style="fill:white; display: none" aria-label="🍎">airplay</span>
<em id="s_cspot" class="fab fa-spotify" style="fill:white; display: inline"></em>
<span data-bs-toggle="tooltip" id="o_type" data-bs-placement="top" title="">
<span id="o_bt" class="material-icons" style="fill:white; display: none" aria-label="">bluetooth</span>
<span id="o_spdif" class="material-icons" style="fill:white; display: none" aria-label="">graphic_eq</span>
<span id="o_spdif" class="material-icons" style="fill:white; display: none"
aria-label="">graphic_eq</span>
<span id="o_i2s" class="material-icons" style="fill:white; display: none" aria-label="🔈">speaker</span>
</span>
<span id="ethernet" class="material-icons if_eth" style="fill:white; display: none" aria-label="ETH">cable</span>
<span id="wifiStsIcon" class="material-icons if_wifi"
style="fill:white; display: none" aria-label=""></span>
<span id="ethernet" class="material-icons if_eth" style="fill:white; display: none"
aria-label="ETH">cable</span>
<span id="wifiStsIcon" class="material-icons if_wifi" style="fill:white; display: none"
aria-label=""></span>
</div>
</header>
@@ -215,33 +219,116 @@
<div class="card text-white mb-3">
<div class="card-header">Usage Templates</div>
<div class="card-body">
<fieldset>
<fieldset class="form-group" id="output-tmpl">
<legend>Output</legend>
<div class="form-check">
<label>Output</label><br>
<div class="form-check form-check-inline">
<label class="form-check-label">
<input type="radio" class="form-check-input" name="output-tmpl" id="i2s">
I2S Dac
</label>
</div>
<div class="form-check">
<div class="form-check form-check-inline">
<label class="form-check-label">
<input type="radio" class="form-check-input" name="output-tmpl" id="spdif">
SPDIF
</label>
</div>
<div class="form-check">
<div class="form-check form-check-inline">
<label class="form-check-label">
<input type="radio" class="form-check-input" name="output-tmpl" id="bt">
Bluetooth
</label>
</div>
</fieldset>
<div class="form-group"><label for="player">Player Name</label><input type="text"
class="form-control " placeholder="Squeezelite" id="player"></div>
<div class="form-group"><label for="optional">Optional setting (e.g. for LMS IP
address)</label><input type="text" class="form-control" id="optional"></div>
<fieldset>
<div id="options">
<div class="form-group"><label for="cmd_opt_n">Set the player name</label><input
type="text" class="form-control sqcmd" placeholder="name" id="cmd_opt_n"></div>
<div class="form-group"><label for="cmd_opt_s">Server</label><input type="text"
class="form-control sqcmd" placeholder="server[:port]" id="cmd_opt_s"></div>
<div class="form-group"><label for="cmd_opt_b">Stream and Output buffer sizes (in
Kbytes)</label><input type="text" class="form-control sqcmd"
placeholder="stream:output" id="cmd_opt_b"></div>
<div class="form-group"><label for="cmd_opt_c">Restrict codecs </label><input
type="text" class="form-control sqcmd" placeholder="codec1,codec2"
id="cmd_opt_c"><small class="form-text text-muted">Supported: flac,pcm,mp3,ogg
(mad,mpg for specific mp3 codec)</small></div>
<div class="form-group"><label for="cmd_opt_C">Ouput device close timeout</label><input
type="text" class="form-control sqcmd" placeholder="timeout"
id="cmd_opt_C"><small class="form-text text-muted">Close output device after
timeout seconds, default
is to keep it open while player is 'on'</small></div>
<div class="form-group"><label for="cmd_opt_d">Set logging level</label><input
type="text" class="form-control sqcmd" placeholder="log=level"
id="cmd_opt_d"><small class="form-text text-muted">Logs:
all|slimproto|stream|decode|output, level:
info|debug|sdebug</small></div>
<div class="form-group"><label for="cmd_opt_e">Explicitly exclude native support of one
or more codecs</label><input type="text" class="form-control sqcmd"
placeholder="codec1,codec2" id="cmd_opt_e"><small
class="form-text text-muted">Supported: flac,pcm,mp3,ogg (mad,mpg for specific
mp3 codec)</small></div>
<div class="form-group"><label for="cmd_opt_m">Set mac address</label><input type="text"
class="form-control sqcmd" placeholder="mac addr" id="cmd_opt_m"><small
class="form-text text-muted">Format: ab:cd:ef:12:34:56</small></div>
<div class="form-group"><label for="cmd_opt_r">Sample rates supported, allows output to
be off when squeezelite is started</label><input type="text"
class="form-control sqcmd" placeholder="rates" id="cmd_opt_r"><small
class="form-text text-muted">&lt;maxrate&gt;|&lt;minrate&gt;&lt;maxrate&gt;&lt;rate1&gt;&lt;rate2&gt;&lt;rate3&gt;</small>
</div>
<div class="form-group hide" id="cmd_opt_R">
<label>Resample</label><br>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample_none"
suffix="" checked aint="false">
<label class="form-check-label" for="resampleNone">No resampling</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample"
suffix=' -R' aint="false">
<label class="form-check-label" for="resampleNone">Default</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample_b"
suffix=' -R -u b' aint="true">
<label class="form-check-label" for="resampleBasic">Basic linear
interpolation</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample_l"
suffix=' -R -u l' aint="true">
<label class="form-check-label" for="resample13Taps">13 taps</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="resample" id="resample_m"
suffix=' -R -u m' aint="true">
<label class="form-check-label" for="resample21Taps">21 taps</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="checkbox" name="interpolate"
id="resample_i" suffix=":i">
<label class="form-check-label" for="interpolate">Interpolate filter
coefficients</label>
</div>
</div>
<div class="form-group"><label for="cmd_opt_Z">Report rate to server in helo as the
maximum sample rate we can support</label><input type="text"
class="form-control" placeholder="rate" id="cmd_opt_Z"></div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" id="cmd_opt_W" value=""
checked="">
Read wave and aiff format from header, ignore server parameters
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" id="disable-squeezelite"

View File

@@ -28,71 +28,95 @@ Object.assign(Date.prototype, {
return this.toLocaleString(undefined, opt);
},
});
function handleNVSVisible(){
function get_control_option_value(obj) {
let ctrl,id,val,opt;
let radio = false;
let checked = false;
if (typeof (obj) === 'string') {
id = obj;
ctrl = $(`#${id}`);
} else {
id = $(obj).attr('id');
ctrl = $(obj);
}
if(ctrl.attr('type') === 'checkbox'){
opt = $(obj).checked?id.replace('cmd_opt_', ''):'';
val = true;
}
else {
opt = id.replace('cmd_opt_', '');
val = $(obj).val();
val = `${val.includes(" ") ? '"' : ''}${val}${val.includes(" ") ? '"' : ''}`;
}
return { opt, val };
}
function handleNVSVisible() {
let nvs_previous_checked = isEnabled(Cookies.get("show-nvs"));
$('input#show-nvs')[0].checked = nvs_previous_checked ;
$('input#show-nvs')[0].checked = nvs_previous_checked;
if ($('input#show-nvs')[0].checked || recovery) {
$('*[href*="-nvs"]').show();
} else {
$('*[href*="-nvs"]').hide();
$('*[href*="-nvs"]').show();
} else {
$('*[href*="-nvs"]').hide();
}
}
function concatenateOptions(options) {
let commandLine = ' ';
for (const [option, value] of Object.entries(options)) {
if (option !== 'n' && option !== 'o') {
commandLine += `-${option} `;
if (value !== true) {
commandLine += `${value} `;
}
}
}
return commandLine;
}
function isEnabled(val) {
return val!=undefined && typeof val === 'string' && val.match("[Yy1]");
return val != undefined && typeof val === 'string' && val.match("[Yy1]");
}
const nvsTypes = {
NVS_TYPE_U8: 0x01,
/*! < Type uint8_t */
NVS_TYPE_I8: 0x11,
/*! < Type int8_t */
NVS_TYPE_U16: 0x02,
/*! < Type uint16_t */
NVS_TYPE_I16: 0x12,
/*! < Type int16_t */
NVS_TYPE_U32: 0x04,
/*! < Type uint32_t */
NVS_TYPE_I32: 0x14,
/*! < Type int32_t */
NVS_TYPE_U64: 0x08,
/*! < Type uint64_t */
NVS_TYPE_I64: 0x18,
/*! < Type int64_t */
NVS_TYPE_STR: 0x21,
/*! < Type string */
NVS_TYPE_BLOB: 0x42,
/*! < Type blob */
NVS_TYPE_ANY: 0xff /*! < Must be last */,
};
const btIcons = {
bt_playing: {'label':'','icon': 'media_bluetooth_on'},
bt_disconnected: {'label':'','icon': 'media_bluetooth_off'},
bt_neutral: {'label':'','icon': 'bluetooth'},
bt_connecting: {'label':'','icon': 'bluetooth_searching'},
bt_connected: {'label':'','icon': 'bluetooth_connected'},
bt_disabled: {'label':'','icon': 'bluetooth_disabled'},
play_arrow: {'label':'','icon': 'play_circle_filled'},
pause: {'label':'','icon': 'pause_circle'},
stop: {'label':'','icon': 'stop_circle'},
'': {'label':'','icon':''}
bt_playing: { 'label': '', 'icon': 'media_bluetooth_on' },
bt_disconnected: { 'label': '', 'icon': 'media_bluetooth_off' },
bt_neutral: { 'label': '', 'icon': 'bluetooth' },
bt_connecting: { 'label': '', 'icon': 'bluetooth_searching' },
bt_connected: { 'label': '', 'icon': 'bluetooth_connected' },
bt_disabled: { 'label': '', 'icon': 'bluetooth_disabled' },
play_arrow: { 'label': '', 'icon': 'play_circle_filled' },
pause: { 'label': '', 'icon': 'pause_circle' },
stop: { 'label': '', 'icon': 'stop_circle' },
'': { 'label': '', 'icon': '' }
};
const batIcons = [
{ icon: "battery_0_bar", label:'▪', ranges: [{ f: 5.8, t: 6.8 }, { f: 8.8, t: 10.2 }] },
{ icon: "battery_2_bar", label:'▪▪', ranges: [{ f: 6.8, t: 7.4 }, { f: 10.2, t: 11.1 }] },
{ icon: "battery_3_bar", label:'▪▪▪', ranges: [{ f: 7.4, t: 7.5 }, { f: 11.1, t: 11.25 }] },
{ icon: "battery_4_bar", label:'▪▪▪▪', ranges: [{ f: 7.5, t: 7.8 }, { f: 11.25, t: 11.7 }] }
{ icon: "battery_0_bar", label: '▪', ranges: [{ f: 5.8, t: 6.8 }, { f: 8.8, t: 10.2 }] },
{ icon: "battery_2_bar", label: '▪▪', ranges: [{ f: 6.8, t: 7.4 }, { f: 10.2, t: 11.1 }] },
{ icon: "battery_3_bar", label: '▪▪▪', ranges: [{ f: 7.4, t: 7.5 }, { f: 11.1, t: 11.25 }] },
{ icon: "battery_4_bar", label: '▪▪▪▪', ranges: [{ f: 7.5, t: 7.8 }, { f: 11.25, t: 11.7 }] }
];
const btStateIcons = [
{ desc: 'Idle', sub: ['bt_neutral'] },
@@ -162,7 +186,7 @@ let flashState = {
$('.flact').prop('disabled', false);
$('#flashfilename').value = null;
$('#fw-url-input').value = null;
if(!this.isStateError()){
if (!this.isStateError()) {
$('span#flash-status').html('');
$('#fwProgressLabel').parent().removeClass('bg-danger');
}
@@ -357,8 +381,8 @@ let flashState = {
const xhttp = new XMLHttpRequest();
xhttp.context = this;
var boundHandleUploadProgressEvent = this.HandleUploadProgressEvent.bind(this);
var boundsetOTAError=this.setOTAError.bind(this);
xhttp.upload.addEventListener("progress",boundHandleUploadProgressEvent, false);
var boundsetOTAError = this.setOTAError.bind(this);
xhttp.upload.addEventListener("progress", boundHandleUploadProgressEvent, false);
xhttp.onreadystatechange = function () {
if (xhttp.readyState === 4) {
if (xhttp.status === 0 || xhttp.status === 404) {
@@ -464,11 +488,89 @@ window.handleReboot = function (link) {
$('#reboot_nav').removeClass('active'); delayReboot(500, '', link);
}
}
function isConnected(){
return ConnectedTo.hasOwnProperty('ip') && ConnectedTo.ip!='0.0.0.0'&& ConnectedTo.ip!='';
function parseSqueezeliteCommandLine(commandLine) {
const options = {};
let output, name;
let otherValues = '';
const argRegex = /("[^"]+"|'[^']+'|\S+)/g;
const args = commandLine.match(argRegex);
let i = 0;
while (i < args.length) {
const arg = args[i];
if (arg.startsWith('-')) {
const option = arg.slice(1);
if (option === '') {
otherValues += args.slice(i).join(' ');
break;
}
let value = true;
if (i + 1 < args.length && !args[i + 1].startsWith('-')) {
value = args[i + 1].replace(/"/g, '').replace(/'/g, '');
i++;
}
options[option] = value;
} else {
otherValues += arg + ' ';
}
i++;
}
otherValues = otherValues.trim();
output = getOutput(options);
name = getName(options);
let otherOptions={btname:null,n:null};
// assign o and n options to otheroptions if present
if (options.o && output.toUpperCase() === 'BT') {
let temp = parseSqueezeliteCommandLine(options.o);
if(temp.name) {
otherOptions.btname = temp.name;
}
delete options.o;
}
if (options.n) {
otherOptions['n'] = options.n;
delete options.n;
}
return { name, output, options, otherValues,otherOptions };
}
function getIcon(icons){
return isConnected()?icons.icon:icons.label;
function getOutput(options) {
let output;
if (options.o){
output = options.o.replace(/"/g, '').replace(/'/g, '');
/* set output as the first alphanumerical word in the command line */
if (output.indexOf(' ') > 0) {
output = output.substring(0, output.indexOf(' '));
}
}
return output;
}
function getName(options) {
let name;
/* if n option present, assign to name variable */
if (options.n){
name = options.n.replace(/"/g, '').replace(/'/g, '');
}
return name;
}
function isConnected() {
return ConnectedTo.hasOwnProperty('ip') && ConnectedTo.ip != '0.0.0.0' && ConnectedTo.ip != '';
}
function getIcon(icons) {
return isConnected() ? icons.icon : icons.label;
}
function handlebtstate(data) {
let icon = '';
@@ -476,7 +578,7 @@ function handlebtstate(data) {
if (data.bt_status !== undefined && data.bt_sub_status !== undefined) {
const iconindex = btStateIcons[data.bt_status].sub[data.bt_sub_status];
if (iconindex) {
icon = btIcons[iconindex];
icon = btIcons[iconindex];
tt = btStateIcons[data.bt_status].desc;
} else {
icon = btIcons.bt_connected;
@@ -485,19 +587,28 @@ function handlebtstate(data) {
}
$('#o_type').attr('title', tt);
$('#o_bt').html(isConnected()?icon.label:icon.text);
$('#o_bt').html(isConnected() ? icon.label : icon.text);
}
function handleTemplateTypeRadio(outtype) {
$('#o_type').children('span').css({ display: 'none' });
let changed = false;
if (outtype === 'bt') {
changed = output !== 'bt' && output !== '';
output = 'bt';
} else if (outtype === 'spdif') {
changed = output !== 'spdif' && output !== '';
output = 'spdif';
} else {
changed = output !== 'i2s' && output !== '';
output = 'i2s';
}
$('#' + output).prop('checked', true);
$('#o_' + output).css({ display: 'inline' });
if (changed) {
Object.keys(commandDefaults[output]).forEach(function (key) {
$(`#cmd_opt_${key}`).val(commandDefaults[output][key]);
});
}
}
function handleExceptionResponse(xhr, _ajaxOptions, thrownError) {
@@ -545,7 +656,17 @@ let releaseURL =
let recovery = false;
let messagesHeld = false;
const commandHeader = 'squeezelite -b 500:2000 -d all=info -C 30 -W';
let commandBTSinkName = '';
const commandHeader = 'squeezelite ';
const commandDefaults = {
i2s: { b: "500:2000", C: "30", W: "", Z: "96000", o: "I2S" },
spdif: { b: "500:2000", C: "30", W: "", Z: "48000", o: "SPDIF" },
bt: { b: "500:2000", C: "30", W: "", Z: "44100", o: "BT" },
};
let validOptions = {
codecs: ['flac', 'pcm', 'mp3', 'ogg', 'aac', 'wma', 'alac', 'dsd', 'mad', 'mpg']
};
//let blockFlashButton = false;
let apList = null;
//let selectedSSID = '';
@@ -559,6 +680,7 @@ let hostName = '';
let versionName = 'Squeezelite-ESP32';
let prevmessage = '';
let project_name = versionName;
let depth = 16;
let board_model = '';
let platform_name = versionName;
let preset_name = '';
@@ -690,11 +812,6 @@ function handleHWPreset(allfields, reboot) {
}
},
});
}
@@ -773,23 +890,34 @@ function delayReboot(duration, cmdname, ota = 'reboot') {
// eslint-disable-next-line no-unused-vars
window.saveAutoexec1 = function (apply) {
showCmdMessage('cfg-audio-tmpl', 'MESSAGING_INFO', 'Saving.\n', false);
let commandLine = commandHeader + ' -n "' + $('#player').val() + '"';
let commandLine = `${commandHeader} -o ${output} `;
$('.sqcmd').each(function () {
let { opt, val } = get_control_option_value($(this));
if ((opt && opt.length>0 ) && typeof(val) == 'boolean' || val.length > 0) {
const optStr=opt===':'?opt:(` -${opt} `);
val = typeof(val) == 'boolean'?'':val;
commandLine += `${optStr} ${val}`;
}
});
const resample=$('#cmd_opt_R input[name=resample]:checked');
if (resample.length>0 && resample.attr('suffix')!=='') {
commandLine += resample.attr('suffix');
// now check resample_i option and if checked, add suffix to command line
if ($('#resample_i').is(":checked") && resample.attr('aint') =='true') {
commandLine += $('#resample_i').attr('suffix');
}
}
if (output === 'bt') {
commandLine += ' -o "BT" -R -Z 192000';
showCmdMessage(
'cfg-audio-tmpl',
'MESSAGING_INFO',
'Remember to configure the Bluetooth audio device name.\n',
true
);
} else if (output === 'spdif') {
commandLine += ' -o SPDIF -Z 192000';
} else {
commandLine += ' -o I2S';
}
if ($('#optional').val() !== '') {
commandLine += ' ' + $('#optional').val();
}
commandLine += concatenateOptions(options);
const data = {
timestamp: Date.now(),
};
@@ -880,9 +1008,33 @@ window.handleConnect = function () {
// now we can re-set the intervals regardless of result
}
function renderError(opt,error){
const fieldname = `cmd_opt_${opt}`;
let errorFieldName=`${fieldname}-error`;
let errorField=$(`#${errorFieldName}`);
let field=$(`#${fieldname}`);
if (!errorField || errorField.length ==0) {
field.after(`<div id="${errorFieldName}" class="invalid-feedback"></div>`);
errorField=$(`#${errorFieldName}`);
}
if(error.length ==0){
errorField.hide();
field.removeClass('is-invalid');
field.addClass('is-valid');
errorField.text('');
}
else {
errorField.show();
errorField.text(error);
field.removeClass('is-valid');
field.addClass('is-invalid');
}
return errorField;
}
$(document).ready(function () {
$('.material-icons').each(function (_index, entry) {
entry.attributes['icon']=entry.textContent;
entry.attributes['icon'] = entry.textContent;
});
setIcons(true);
handleNVSVisible();
@@ -908,6 +1060,43 @@ $(document).ready(function () {
});
setTimeout(refreshAP, 1500);
/* add validation for cmd_opt_c, which accepts a comma separated list.
getting known codecs from validOptions.codecs array
use bootstrap classes to highlight the error with an overlay message */
$('#options input').on('input', function () {
const { opt, val } = get_control_option_value(this);
if (opt === 'c' || opt === 'e') {
const fieldname = `cmd_opt_${opt}_codec-error`;
const values = val.split(',').map(function (item) {
return item.trim();
});
/* get a list of invalid codecs */
const invalid = values.filter(function (item) {
return !validOptions.codecs.includes(item);
});
renderError(opt,invalid.length > 0 ? `Invalid codec(s) ${invalid.join(', ')}` : '');
}
/* add validation for cmd_opt_m, which accepts a mac_address */
if (opt === 'm') {
const mac_regex = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;
renderError(opt,mac_regex.test(val) ? '' : 'Invalid MAC address');
}
if (opt === 'r') {
const rateRegex = /^(\d+\.?\d*|\.\d+)-(\d+\.?\d*|\.\d+)$|^(\d+\.?\d*)$|^(\d+\.?\d*,)+\d+\.?\d*$/;
renderError(opt,rateRegex.test(val)?'':`Invalid rate(s) ${val}. Acceptable format: <maxrate>|<minrate>-<maxrate>|<rate1>,<rate2>,<rate3>`);
}
}
);
$('#WifiConnectDialog')[0].addEventListener('shown.bs.modal', function (event) {
$("*[class*='connecting']").hide();
@@ -1029,7 +1218,7 @@ $(document).ready(function () {
$('input#show-nvs').on('click', function () {
this.checked = this.checked ? 1 : 0;
Cookies.set("show-nvs", this.checked?'Y':'N');
Cookies.set("show-nvs", this.checked ? 'Y' : 'N');
handleNVSVisible();
});
$('#btn_reboot_recovery').on('click', function () {
@@ -1048,7 +1237,7 @@ $(document).ready(function () {
saveAutoexec1(true);
});
$('#btn_disconnect').on('click', function () {
ConnectedTo={};
ConnectedTo = {};
refreshAPHTML2();
$.ajax({
url: '/connect.json',
@@ -1303,15 +1492,15 @@ window.setURL = function (button) {
function rssiToIcon(rssi) {
if (rssi >= -55) {
return {'label':'****','icon':`signal_wifi_statusbar_4_bar`};
return { 'label': '****', 'icon': `signal_wifi_statusbar_4_bar` };
} else if (rssi >= -60) {
return {'label':'***','icon':`network_wifi_3_bar`};
return { 'label': '***', 'icon': `network_wifi_3_bar` };
} else if (rssi >= -65) {
return {'label':'**','icon':`network_wifi_2_bar`};
return { 'label': '**', 'icon': `network_wifi_2_bar` };
} else if (rssi >= -70) {
return {'label':'*','icon':`network_wifi_1_bar`};
return { 'label': '*', 'icon': `network_wifi_1_bar` };
} else {
return {'label':'.','icon':`signal_wifi_statusbar_null`};
return { 'label': '.', 'icon': `signal_wifi_statusbar_null` };
}
}
@@ -1339,9 +1528,9 @@ function refreshAP() {
});
}
function formatAP(ssid, rssi, auth) {
const rssi_icon=rssiToIcon(rssi);
const auth_icon={label:auth == 0 ? '🔓' : '🔒',icon:auth == 0 ? 'no_encryption' : 'lock'};
const rssi_icon = rssiToIcon(rssi);
const auth_icon = { label: auth == 0 ? '🔓' : '🔒', icon: auth == 0 ? 'no_encryption' : 'lock' };
return `<tr data-bs-toggle="modal" data-bs-target="#WifiConnectDialog"><td></td><td>${ssid}</td><td>
<span class="material-icons" style="fill:white; display: inline" aria-label="${rssi_icon.label}" icon="${rssi_icon.icon}" >${getIcon(rssi_icon)}</span>
</td><td>
@@ -1512,9 +1701,9 @@ function getMessages() {
break;
}
}
setTimeout(getMessages,messageInterval);
setTimeout(getMessages, messageInterval);
}).fail(function (xhr, ajaxOptions, thrownError) {
if (xhr.status == 404) {
$('.orec').hide(); // system commands won't be available either
messagesHeld = true;
@@ -1522,15 +1711,15 @@ function getMessages() {
else {
handleExceptionResponse(xhr, ajaxOptions, thrownError);
}
if(xhr.status == 0 && xhr.readyState ==0){
if (xhr.status == 0 && xhr.readyState == 0) {
// probably a timeout. Target is rebooting?
setTimeout(getMessages,messageInterval*2); // increase duration if a failure happens
setTimeout(getMessages, messageInterval * 2); // increase duration if a failure happens
}
else if(!messagesHeld){
else if (!messagesHeld) {
// 404 here means we rebooted to an old recovery
setTimeout(getMessages,messageInterval); // increase duration if a failure happens
setTimeout(getMessages, messageInterval); // increase duration if a failure happens
}
}
);
@@ -1552,18 +1741,18 @@ function handleRecoveryMode(data) {
$('#boot-button').html('Reboot');
$('#boot-form').attr('action', '/reboot_ota.json');
} else {
if(!recovery && messagesHeld){
messagesHeld=false;
setTimeout(getMessages,messageInterval); // increase duration if a failure happens
if (!recovery && messagesHeld) {
messagesHeld = false;
setTimeout(getMessages, messageInterval); // increase duration if a failure happens
}
recovery = false;
$('.recovery_element').hide();
$('.ota_element').show();
$('#boot-button').html('Recovery');
$('#boot-form').attr('action', '/recovery.json');
}
}
function hasConnectionChanged(data) {
@@ -1645,9 +1834,9 @@ function handleWifiDialog(data) {
}
}
function setIcons(offline){
function setIcons(offline) {
$('.material-icons').each(function (_index, entry) {
entry.textContent = entry.attributes[offline?'aria-label':'icon'].value;
entry.textContent = entry.attributes[offline ? 'aria-label' : 'icon'].value;
});
}
function handleNetworkStatus(data) {
@@ -1682,13 +1871,13 @@ function batteryToIcon(voltage) {
for (const iconEntry of batIcons) {
for (const entryRanges of iconEntry.ranges) {
if (inRange(voltage, entryRanges.f, entryRanges.t)) {
return { label: iconEntry.label, icon:iconEntry.icon};
return { label: iconEntry.label, icon: iconEntry.icon };
}
}
}
return {label:'▪▪▪▪',icon:"battery_full"};
return { label: '▪▪▪▪', icon: "battery_full" };
}
function checkStatus() {
$.ajaxSetup({
@@ -1700,6 +1889,16 @@ function checkStatus() {
handleNetworkStatus(data);
handlebtstate(data);
flashState.EventTargetStatus(data);
if(data.depth) {
depth = data.depth;
if(depth==16){
$('#cmd_opt_R').show();
}
else{
$('#cmd_opt_R').hide();
}
}
if (data.project_name && data.project_name !== '') {
project_name = data.project_name;
@@ -1717,10 +1916,10 @@ function checkStatus() {
$('span#flash-status').html('');
}
if (data.Voltage) {
const bat_icon=batteryToIcon(data.Voltage);
const bat_icon = batteryToIcon(data.Voltage);
$('#battery').html(`${getIcon(bat_icon)}`);
$('#battery').attr("aria-label",bat_icon.label);
$('#battery').attr("icon",bat_icon.icon);
$('#battery').attr("aria-label", bat_icon.label);
$('#battery').attr("icon", bat_icon.icon);
$('#battery').show();
} else {
$('#battery').hide();
@@ -1757,15 +1956,15 @@ function checkStatus() {
});
}
$('#o_jack').css({ display: Number(data.Jack) ? 'inline' : 'none' });
setTimeout(checkStatus,statusInterval);
setTimeout(checkStatus, statusInterval);
}).fail(function (xhr, ajaxOptions, thrownError) {
handleExceptionResponse(xhr, ajaxOptions, thrownError);
if(xhr.status == 0 && xhr.readyState ==0){
if (xhr.status == 0 && xhr.readyState == 0) {
// probably a timeout. Target is rebooting?
setTimeout(checkStatus,messageInterval*2); // increase duration if a failure happens
setTimeout(checkStatus, messageInterval * 2); // increase duration if a failure happens
}
else {
setTimeout(checkStatus,messageInterval); // increase duration if a failure happens
setTimeout(checkStatus, messageInterval); // increase duration if a failure happens
}
});
}
@@ -1995,6 +2194,7 @@ function getConfig() {
$('#nvsTable tr').remove();
const data = (entries.config ? entries.config : entries);
SystemConfig = data;
commandBTSinkName = '';
Object.keys(data)
.sort()
.forEach(function (key) {
@@ -2006,20 +2206,15 @@ function getConfig() {
$('#disable-squeezelite')[0].checked = false;
}
} else if (key === 'autoexec1') {
const re = /-o\s?(["][^"]*["]|[^-]+)/g;
const m = re.exec(val);
if (m[1].toUpperCase().startsWith('I2S')) {
handleTemplateTypeRadio('i2s');
} else if (m[1].toUpperCase().startsWith('SPDIF')) {
handleTemplateTypeRadio('spdif');
} else if (m[1].toUpperCase().startsWith('"BT')) {
handleTemplateTypeRadio('bt');
}
/* call new function to parse the squeezelite options */
processSqueezeliteCommandLine(val);
} else if (key === 'host_name') {
val = val.replaceAll('"', '');
$('input#dhcp-name1').val(val);
$('input#dhcp-name2').val(val);
$('#player').val(val);
if ($('#cmd_opt_n').length == 0) {
$('#cmd_opt_n').val(val);
}
document.title = val;
hostName = val;
} else if (key === 'rel_api') {
@@ -2054,6 +2249,10 @@ function getConfig() {
);
$('input#' + key).val(data[key].value);
});
if(commandBTSinkName.length > 0) {
// persist the sink name found in the autoexec1 command line
$('#cfg-audio-bt_source-sink_name').val(commandBTSinkName);
}
$('tbody#nvsTable').append(
"<tr><td><input type='text' class='form-control' id='nvs-new-key' placeholder='new key'></td><td><input type='text' class='form-control' id='nvs-new-value' placeholder='new value' nvs_type=33 ></td></tr>"
);
@@ -2083,6 +2282,40 @@ function getConfig() {
handleExceptionResponse(xhr, ajaxOptions, thrownError);
});
}
function processSqueezeliteCommandLine(val) {
const parsed = parseSqueezeliteCommandLine(val);
if (parsed.output.toUpperCase().startsWith('I2S')) {
handleTemplateTypeRadio('i2s');
} else if (parsed.output.toUpperCase().startsWith('SPDIF')) {
handleTemplateTypeRadio('spdif');
} else if (parsed.output.toUpperCase().startsWith('BT')) {
if(parsed.otherOptions.btname){
commandBTSinkName= parsed.otherOptions.btname;
}
handleTemplateTypeRadio('bt');
}
Object.keys(parsed.options).forEach(function (key) {
const option = parsed.options[key];
if (!$(`#cmd_opt_${key}`).hasOwnProperty('checked')) {
$(`#cmd_opt_${key}`).val(option);
} else {
$(`#cmd_opt_${key}`)[0].checked = option;
}
});
if (parsed.options.hasOwnProperty('u')) {
// parse -u v[:i] and check the appropriate radio button with id #resample_v
const [resampleValue, resampleInterpolation] = parsed.options.u.split(':');
$(`#resample_${resampleValue}`).prop('checked', true);
// if resampleinterpolation is set, check resample_i checkbox
if (resampleInterpolation) {
$('#resample_i').prop('checked', true);
}
}
}
function showLocalMessage(message, severity) {
const msg = {
message: message,

View File

@@ -1,5 +1,5 @@
target_add_binary_data( __idf_wifi-manager webapp/dist/css/index.7964a13ec910c36040b8.css.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/css/index.cd56ff129e3113d8cd3a.css.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/favicon-32x32.png BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/index.html.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.44748c.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.44748c.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/index.3a56a6.bundle.js.gz BINARY)
target_add_binary_data( __idf_wifi-manager webapp/dist/js/node_vendors.3a56a6.bundle.js.gz BINARY)

View File

@@ -1,34 +1,34 @@
// Automatically generated. Do not edit manually!.
#include <inttypes.h>
extern const uint8_t _index_7964a13ec910c36040b8_css_gz_start[] asm("_binary_index_7964a13ec910c36040b8_css_gz_start");
extern const uint8_t _index_7964a13ec910c36040b8_css_gz_end[] asm("_binary_index_7964a13ec910c36040b8_css_gz_end");
extern const uint8_t _index_cd56ff129e3113d8cd3a_css_gz_start[] asm("_binary_index_cd56ff129e3113d8cd3a_css_gz_start");
extern const uint8_t _index_cd56ff129e3113d8cd3a_css_gz_end[] asm("_binary_index_cd56ff129e3113d8cd3a_css_gz_end");
extern const uint8_t _favicon_32x32_png_start[] asm("_binary_favicon_32x32_png_start");
extern const uint8_t _favicon_32x32_png_end[] asm("_binary_favicon_32x32_png_end");
extern const uint8_t _index_html_gz_start[] asm("_binary_index_html_gz_start");
extern const uint8_t _index_html_gz_end[] asm("_binary_index_html_gz_end");
extern const uint8_t _index_44748c_bundle_js_gz_start[] asm("_binary_index_44748c_bundle_js_gz_start");
extern const uint8_t _index_44748c_bundle_js_gz_end[] asm("_binary_index_44748c_bundle_js_gz_end");
extern const uint8_t _node_vendors_44748c_bundle_js_gz_start[] asm("_binary_node_vendors_44748c_bundle_js_gz_start");
extern const uint8_t _node_vendors_44748c_bundle_js_gz_end[] asm("_binary_node_vendors_44748c_bundle_js_gz_end");
extern const uint8_t _index_3a56a6_bundle_js_gz_start[] asm("_binary_index_3a56a6_bundle_js_gz_start");
extern const uint8_t _index_3a56a6_bundle_js_gz_end[] asm("_binary_index_3a56a6_bundle_js_gz_end");
extern const uint8_t _node_vendors_3a56a6_bundle_js_gz_start[] asm("_binary_node_vendors_3a56a6_bundle_js_gz_start");
extern const uint8_t _node_vendors_3a56a6_bundle_js_gz_end[] asm("_binary_node_vendors_3a56a6_bundle_js_gz_end");
const char * resource_lookups[] = {
"/css/index.7964a13ec910c36040b8.css.gz",
"/css/index.cd56ff129e3113d8cd3a.css.gz",
"/favicon-32x32.png",
"/index.html.gz",
"/js/index.44748c.bundle.js.gz",
"/js/node_vendors.44748c.bundle.js.gz",
"/js/index.3a56a6.bundle.js.gz",
"/js/node_vendors.3a56a6.bundle.js.gz",
""
};
const uint8_t * resource_map_start[] = {
_index_7964a13ec910c36040b8_css_gz_start,
_index_cd56ff129e3113d8cd3a_css_gz_start,
_favicon_32x32_png_start,
_index_html_gz_start,
_index_44748c_bundle_js_gz_start,
_node_vendors_44748c_bundle_js_gz_start
_index_3a56a6_bundle_js_gz_start,
_node_vendors_3a56a6_bundle_js_gz_start
};
const uint8_t * resource_map_end[] = {
_index_7964a13ec910c36040b8_css_gz_end,
_index_cd56ff129e3113d8cd3a_css_gz_end,
_favicon_32x32_png_end,
_index_html_gz_end,
_index_44748c_bundle_js_gz_end,
_node_vendors_44748c_bundle_js_gz_end
_index_3a56a6_bundle_js_gz_end,
_node_vendors_3a56a6_bundle_js_gz_end
};

View File

@@ -10,6 +10,7 @@ const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const webpack = require("webpack");
const path = require("path");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const globSync = require("glob").sync;
const glob = require('glob');
const { merge } = require('webpack-merge');
@@ -39,7 +40,15 @@ class BuildEventsHook {
module.exports = (env, options) => (
merge(
env.WEBPACK_SERVE ? devserver : {},
env.ANALYZE_SIZE?{ plugins: [ new BundleAnalyzerPlugin() ]}:{},
env.ANALYZE_SIZE?{ plugins: [ new BundleAnalyzerPlugin(
{
analyzerMode: 'static',
generateStatsFile: true,
statsFilename: 'stats.json',
}
) ]}:{},
{
entry:
{
@@ -170,7 +179,6 @@ module.exports = (env, options) => (
],
},
plugins: [
new HtmlWebpackPlugin({
title: 'SqueezeESP32',
template: './src/index.ejs',
@@ -328,6 +336,7 @@ extern const uint8_t * resource_map_end[];`;
optimization: {
minimize: true,
providedExports: true,
usedExports: true,
minimizer: [
new TerserPlugin({
@@ -340,7 +349,14 @@ extern const uint8_t * resource_map_end[];`;
// enable parallel running
parallel: true,
}),
new HtmlMinimizerPlugin(),
new HtmlMinimizerPlugin({
minimizerOptions: {
removeComments: true,
removeOptionalTags: true,
}
}
),
new CssMinimizerPlugin(),
new ImageMinimizerPlugin({
minimizer: {

View File

@@ -1,6 +1,6 @@
/***********************************
webpack_headers
dist/css/index.7964a13ec910c36040b8.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.44748c.bundle.js.gz,dist/js/node_vendors.44748c.bundle.js.gz
dist/css/index.cd56ff129e3113d8cd3a.css.gz,dist/favicon-32x32.png,dist/index.html.gz,dist/js/index.3a56a6.bundle.js.gz,dist/js/node_vendors.3a56a6.bundle.js.gz
***********************************/
#pragma once
#include <inttypes.h>

View File

@@ -6,6 +6,7 @@ const HtmlWebPackPlugin = require('html-webpack-plugin');
const { Command } = require('commander');
let cmdLines= { };
var { parseArgsStringToArgv } = require('string-argv');
const PORT = 9100;
const data = {
messages: require("../mock/messages.json"),
@@ -133,11 +134,10 @@ const connectReturnCode = {
}
module.exports ={
entry: {
test: './src/test.ts',
test: './src/test.ts'
},
devServer: {
static: {
directory: path.resolve(__dirname, './dist'),
staticOptions: {},
@@ -158,11 +158,10 @@ module.exports ={
},
open: true,
compress: true,
port: 9100,
port: PORT,
host: '127.0.0.1',//your ip address
allowedHosts: "all",
headers: {'Access-Control-Allow-Origin': '*',
'Accept-Encoding': 'identity'},
headers: {'Access-Control-Allow-Origin': '*', 'Accept-Encoding': 'identity'},
client: {
logging: "verbose",
// Can be used only for `errors`/`warnings`
@@ -179,8 +178,18 @@ module.exports ={
throw new Error('webpack-dev-server is not defined');
}
const port = devServer.server.address().port;
console.log('Listening on port:', port);
const PORT = devServer.server.address().port;
// get the path to the test page
const compiler = devServer.compiler;
const entry = compiler.options.entry;
const testEntry = entry['test'].import[0];
const testPath = testEntry.replace('./src/', '').replace('.ts', '.html');
// open the test page
import('open').then((open) => open.default(`http://localhost:${PORT}/${testPath}`));
},
onBeforeSetupMiddleware: function (devServer) {

View File

@@ -1,6 +1,5 @@
idf_component_register(SRC_DIRS .
PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets
EMBED_FILES ../server_certs/github.pem
LDFRAGMENTS "linker.lf"
)
#get_target_property(ill ${COMPONENT_LIB} INTERFACE_LINK_LIBRARIES)

View File

@@ -381,13 +381,6 @@ void app_main()
GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "RECOVERY");
}
/*
ESP_LOGI(TAG,"Checking if certificates need to be updated");
update_certificates(false);
MEMTRACE_PRINT_DELTA();
*/
ESP_LOGD(TAG,"Getting firmware OTA URL (if any)");
fwurl = process_ota_url();

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,13 +1,13 @@
-----BEGIN CERTIFICATE-----
MIIFajCCBPCgAwIBAgIQBRiaVOvox+kD4KsNklVF3jAKBggqhkjOPQQDAzBWMQsw
MIIFajCCBPGgAwIBAgIQDNCovsYyz+ZF7KCpsIT7HDAKBggqhkjOPQQDAzBWMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMTAwLgYDVQQDEydEaWdp
Q2VydCBUTFMgSHlicmlkIEVDQyBTSEEzODQgMjAyMCBDQTEwHhcNMjIwMzE1MDAw
MDAwWhcNMjMwMzE1MjM1OTU5WjBmMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
Q2VydCBUTFMgSHlicmlkIEVDQyBTSEEzODQgMjAyMCBDQTEwHhcNMjMwMjE0MDAw
MDAwWhcNMjQwMzE0MjM1OTU5WjBmMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
aWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEVMBMGA1UEChMMR2l0SHVi
LCBJbmMuMRMwEQYDVQQDEwpnaXRodWIuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D
AQcDQgAESrCTcYUh7GI/y3TARsjnANwnSjJLitVRgwgRI1JlxZ1kdZQQn5ltP3v7
KTtYuDdUeEu3PRx3fpDdu2cjMlyA0aOCA44wggOKMB8GA1UdIwQYMBaAFAq8CCkX
jKU5bXoOzjPHLrPt+8N6MB0GA1UdDgQWBBR4qnLGcWloFLVZsZ6LbitAh0I7HjAl
AQcDQgAEo6QDRgPfRlFWy8k5qyLN52xZlnqToPu5QByQMog2xgl2nFD1Vfd2Xmgg
nO4i7YMMFTAQQUReMqyQodWq8uVDs6OCA48wggOLMB8GA1UdIwQYMBaAFAq8CCkX
jKU5bXoOzjPHLrPt+8N6MB0GA1UdDgQWBBTHByd4hfKdM8lMXlZ9XNaOcmfr3jAl
BgNVHREEHjAcggpnaXRodWIuY29tgg53d3cuZ2l0aHViLmNvbTAOBgNVHQ8BAf8E
BAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMIGbBgNVHR8EgZMw
gZAwRqBEoEKGQGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRMU0h5
@@ -17,15 +17,15 @@ cmwwPgYDVR0gBDcwNTAzBgZngQwBAgIwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3
dy5kaWdpY2VydC5jb20vQ1BTMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGG
GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2Nh
Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTSHlicmlkRUNDU0hBMzg0MjAy
MENBMS0xLmNydDAJBgNVHRMEAjAAMIIBfwYKKwYBBAHWeQIEAgSCAW8EggFrAWkA
dgCt9776fP8QyIudPZwePhhqtGcpXc+xDCTKhYY069yCigAAAX+Oi8SRAAAEAwBH
MEUCIAR9cNnvYkZeKs9JElpeXwztYB2yLhtc8bB0rY2ke98nAiEAjiML8HZ7aeVE
P/DkUltwIS4c73VVrG9JguoRrII7gWMAdwA1zxkbv7FsV78PrUxtQsu7ticgJlHq
P+Eq76gDwzvWTAAAAX+Oi8R7AAAEAwBIMEYCIQDNckqvBhup7GpANMf0WPueytL8
u/PBaIAObzNZeNMpOgIhAMjfEtE6AJ2fTjYCFh/BNVKk1mkTwBTavJlGmWomQyaB
AHYAs3N3B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAF/jovErAAABAMA
RzBFAiEA9Uj5Ed/XjQpj/MxQRQjzG0UFQLmgWlc73nnt3CJ7vskCICqHfBKlDz7R
EHdV5Vk8bLMBW1Q6S7Ga2SbFuoVXs6zFMAoGCCqGSM49BAMDA2gAMGUCMCiVhqft
7L/stBmv1XqSRNfE/jG/AqKIbmjGTocNbuQ7kt1Cs7kRg+b3b3C9Ipu5FQIxAM7c
tGKrYDGt0pH8iF6rzbp9Q4HQXMZXkNxg+brjWxnaOVGTDNwNH7048+s/hT9bUQ==
MENBMS0xLmNydDAJBgNVHRMEAjAAMIIBgAYKKwYBBAHWeQIEAgSCAXAEggFsAWoA
dwDuzdBk1dsazsVct520zROiModGfLzs3sNRSFlGcR+1mwAAAYZQ3Rv6AAAEAwBI
MEYCIQDkFq7T4iy6gp+pefJLxpRS7U3gh8xQymmxtI8FdzqU6wIhALWfw/nLD63Q
YPIwG3EFchINvWUfB6mcU0t2lRIEpr8uAHYASLDja9qmRzQP5WoC+p0w6xxSActW
3SyB2bu/qznYhHMAAAGGUN0cKwAABAMARzBFAiAePGAyfiBR9dbhr31N9ZfESC5G
V2uGBTcyTyUENrH3twIhAPwJfsB8A4MmNr2nW+sdE1n2YiCObW+3DTHr2/UR7lvU
AHcAO1N3dT4tuYBOizBbBv5AO2fYT8P0x70ADS1yb+H61BcAAAGGUN0cOgAABAMA
SDBGAiEAzOBr9OZ0+6OSZyFTiywN64PysN0FLeLRyL5jmEsYrDYCIQDu0jtgWiMI
KU6CM0dKcqUWLkaFE23c2iWAhYAHqrFRRzAKBggqhkjOPQQDAwNnADBkAjAE3A3U
3jSZCpwfqOHBdlxi9ASgKTU+wg0qw3FqtfQ31OwLYFdxh0MlNk/HwkjRSWgCMFbQ
vMkXEPvNvv4t30K6xtpG26qmZ+6OiISBIIXMljWnsiYR1gyZnTzIg3AQSw4Vmw==
-----END CERTIFICATE-----

View File

@@ -73,15 +73,15 @@ ce1XR2bFuAJKZTRei9AqPCCcUZlM51Ke92sRKw2Sfh3oius2FkOH6ipjv3U/697E
A7sKPPcw7+uvTPyLNhBzPvOk
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFajCCBPCgAwIBAgIQBRiaVOvox+kD4KsNklVF3jAKBggqhkjOPQQDAzBWMQsw
MIIFajCCBPGgAwIBAgIQDNCovsYyz+ZF7KCpsIT7HDAKBggqhkjOPQQDAzBWMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMTAwLgYDVQQDEydEaWdp
Q2VydCBUTFMgSHlicmlkIEVDQyBTSEEzODQgMjAyMCBDQTEwHhcNMjIwMzE1MDAw
MDAwWhcNMjMwMzE1MjM1OTU5WjBmMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
Q2VydCBUTFMgSHlicmlkIEVDQyBTSEEzODQgMjAyMCBDQTEwHhcNMjMwMjE0MDAw
MDAwWhcNMjQwMzE0MjM1OTU5WjBmMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2Fs
aWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEVMBMGA1UEChMMR2l0SHVi
LCBJbmMuMRMwEQYDVQQDEwpnaXRodWIuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D
AQcDQgAESrCTcYUh7GI/y3TARsjnANwnSjJLitVRgwgRI1JlxZ1kdZQQn5ltP3v7
KTtYuDdUeEu3PRx3fpDdu2cjMlyA0aOCA44wggOKMB8GA1UdIwQYMBaAFAq8CCkX
jKU5bXoOzjPHLrPt+8N6MB0GA1UdDgQWBBR4qnLGcWloFLVZsZ6LbitAh0I7HjAl
AQcDQgAEo6QDRgPfRlFWy8k5qyLN52xZlnqToPu5QByQMog2xgl2nFD1Vfd2Xmgg
nO4i7YMMFTAQQUReMqyQodWq8uVDs6OCA48wggOLMB8GA1UdIwQYMBaAFAq8CCkX
jKU5bXoOzjPHLrPt+8N6MB0GA1UdDgQWBBTHByd4hfKdM8lMXlZ9XNaOcmfr3jAl
BgNVHREEHjAcggpnaXRodWIuY29tgg53d3cuZ2l0aHViLmNvbTAOBgNVHQ8BAf8E
BAMCB4AwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMIGbBgNVHR8EgZMw
gZAwRqBEoEKGQGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRMU0h5
@@ -91,123 +91,98 @@ cmwwPgYDVR0gBDcwNTAzBgZngQwBAgIwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3
dy5kaWdpY2VydC5jb20vQ1BTMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGG
GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2Nh
Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTSHlicmlkRUNDU0hBMzg0MjAy
MENBMS0xLmNydDAJBgNVHRMEAjAAMIIBfwYKKwYBBAHWeQIEAgSCAW8EggFrAWkA
dgCt9776fP8QyIudPZwePhhqtGcpXc+xDCTKhYY069yCigAAAX+Oi8SRAAAEAwBH
MEUCIAR9cNnvYkZeKs9JElpeXwztYB2yLhtc8bB0rY2ke98nAiEAjiML8HZ7aeVE
P/DkUltwIS4c73VVrG9JguoRrII7gWMAdwA1zxkbv7FsV78PrUxtQsu7ticgJlHq
P+Eq76gDwzvWTAAAAX+Oi8R7AAAEAwBIMEYCIQDNckqvBhup7GpANMf0WPueytL8
u/PBaIAObzNZeNMpOgIhAMjfEtE6AJ2fTjYCFh/BNVKk1mkTwBTavJlGmWomQyaB
AHYAs3N3B+GEUPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAF/jovErAAABAMA
RzBFAiEA9Uj5Ed/XjQpj/MxQRQjzG0UFQLmgWlc73nnt3CJ7vskCICqHfBKlDz7R
EHdV5Vk8bLMBW1Q6S7Ga2SbFuoVXs6zFMAoGCCqGSM49BAMDA2gAMGUCMCiVhqft
7L/stBmv1XqSRNfE/jG/AqKIbmjGTocNbuQ7kt1Cs7kRg+b3b3C9Ipu5FQIxAM7c
tGKrYDGt0pH8iF6rzbp9Q4HQXMZXkNxg+brjWxnaOVGTDNwNH7048+s/hT9bUQ==
MENBMS0xLmNydDAJBgNVHRMEAjAAMIIBgAYKKwYBBAHWeQIEAgSCAXAEggFsAWoA
dwDuzdBk1dsazsVct520zROiModGfLzs3sNRSFlGcR+1mwAAAYZQ3Rv6AAAEAwBI
MEYCIQDkFq7T4iy6gp+pefJLxpRS7U3gh8xQymmxtI8FdzqU6wIhALWfw/nLD63Q
YPIwG3EFchINvWUfB6mcU0t2lRIEpr8uAHYASLDja9qmRzQP5WoC+p0w6xxSActW
3SyB2bu/qznYhHMAAAGGUN0cKwAABAMARzBFAiAePGAyfiBR9dbhr31N9ZfESC5G
V2uGBTcyTyUENrH3twIhAPwJfsB8A4MmNr2nW+sdE1n2YiCObW+3DTHr2/UR7lvU
AHcAO1N3dT4tuYBOizBbBv5AO2fYT8P0x70ADS1yb+H61BcAAAGGUN0cOgAABAMA
SDBGAiEAzOBr9OZ0+6OSZyFTiywN64PysN0FLeLRyL5jmEsYrDYCIQDu0jtgWiMI
KU6CM0dKcqUWLkaFE23c2iWAhYAHqrFRRzAKBggqhkjOPQQDAwNnADBkAjAE3A3U
3jSZCpwfqOHBdlxi9ASgKTU+wg0qw3FqtfQ31OwLYFdxh0MlNk/HwkjRSWgCMFbQ
vMkXEPvNvv4t30K6xtpG26qmZ+6OiISBIIXMljWnsiYR1gyZnTzIg3AQSw4Vmw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIHFDCCBfygAwIBAgIQCLS/dX/bKN3zuMTJNXxaSTANBgkqhkiG9w0BAQsFADBP
MIIHEjCCBfqgAwIBAgIQBE1y13zdpwLdWmfyoju92TANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSkwJwYDVQQDEyBE
aWdpQ2VydCBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTAeFw0yMjA0MDcwMDAwMDBa
Fw0yMzA0MDcyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y
aWdpQ2VydCBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTAeFw0yMzAyMjEwMDAwMDBa
Fw0yNDAzMjAyMzU5NTlaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRUwEwYDVQQKEwxHaXRIdWIsIElu
Yy4xFTATBgNVBAMMDCouZ2l0aHViLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBALyqZjatk2jnqiWmp6eusW70yJlreKz8mllyRSPxnIVeuwCHGzeQ
pGOOZkdRiBLcC2SWM3WgwQjBVBzqS1hWgoP5e6hzuXvGM3anlgJDE9dDUJfdC/Is
nzB4Q5Y4TU3FcRCUaK4GMoJGC0fu0fDbH927yKAnvdErG4u+jFSqIidwEaEfPWCC
o3xCyQLHTknXQ9aaDvU6GHNX0us6G+bjdErIwQtC56F0ke7biV0A/DWX5V+hVsVY
jY9JbYNx+KFjmUxLibccXzXs0pJ+a6Xa4OhhrFebPwS+SQA+gxTTvZotj4J5kf2l
nM9H+1whu6I5qPebhlTRTKpxdPm9V647Zj8CAwEAAaOCA9EwggPNMB8GA1UdIwQY
MBaAFLdrouqoqoSMeeq02g+YssWVdrn0MB0GA1UdDgQWBBRWmrM0shNZi0idiZiI
7l3ryIMwdDB7BgNVHREEdDByggwqLmdpdGh1Yi5jb22CDnd3dy5naXRodWIuY29t
gglnaXRodWIuaW+CCmdpdGh1Yi5jb22CCyouZ2l0aHViLmlvghVnaXRodWJ1c2Vy
Y29udGVudC5jb22CFyouZ2l0aHVidXNlcmNvbnRlbnQuY29tMA4GA1UdDwEB/wQE
AwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwgY8GA1UdHwSBhzCB
hDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTUlNB
U0hBMjU2MjAyMENBMS00LmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQu
Y29tL0RpZ2lDZXJ0VExTUlNBU0hBMjU2MjAyMENBMS00LmNybDA+BgNVHSAENzA1
MDMGBmeBDAECAjApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNv
bS9DUFMwfwYIKwYBBQUHAQEEczBxMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k
aWdpY2VydC5jb20wSQYIKwYBBQUHMAKGPWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0
LmNvbS9EaWdpQ2VydFRMU1JTQVNIQTI1NjIwMjBDQTEtMS5jcnQwCQYDVR0TBAIw
ADCCAX8GCisGAQQB1nkCBAIEggFvBIIBawFpAHYA6D7Q2j71BjUy51covIlryQPT
y9ERa+zraeF3fW0GvW4AAAGABfvdbAAABAMARzBFAiAGLk49aFP9ARwPXCa59WnI
f5jIU5eFmqR6/W3Zm38KiwIhAIp8FySKqbKk600uO4iPsS6TW8hJl67PprwXYMlr
o3wPAHcANc8ZG7+xbFe/D61MbULLu7YnICZR6j/hKu+oA8M71kwAAAGABfvdXQAA
BAMASDBGAiEAjFarHnzcbBvQ8//um0zVd4G3T5zbW4XSUIJSTc5JGo8CIQDaT5K8
pji9egTYSypP9XfRK+Z2wID3j43uuGjiKSOKyQB2ALNzdwfhhFD4Y4bWBancEQlK
eS2xZwwLh9zwAw55NqWaAAABgAX73YsAAAQDAEcwRQIhAO/PWksY7Zd7W5NJr3e4
xRkx8J6Qv7a33VA3tkm96k4WAiBshJWPE2BjKzuQ/KEfiKnvD4dDa3btkmcWlpiD
R8AvQDANBgkqhkiG9w0BAQsFAAOCAQEARtY8iVMqqBCXGZj2NRhpxA4eS2b/e/56
JhnRWGz3wxf0aRjbaZ2sUH3aHe1UDyg4jVPgnSLsGnBMmN5Rk32uiB/5v6/uRhCa
l26Yi9MYbeQpt0980MxT5hhv8bThRiNa77+oAOcrYMJEGIf2/9k0yoefblEZTR02
6UU6pkDhxjMtpyNRr+IdqQM/4lCM6nu8FZ/qaLltvta1Enq+jEwEObo/PoBoQJzJ
j7hcu7rkyPQIK1raQ9pK7uFJ2/FgtxIUuT+by06LnUp82VB7QxlniXO2R4XgDzWd
umlpkAFJQvZ+Sa2rSdjynrTDedjQIv3s1jH2Tvao5fR23tW2XAQhVg==
Yy4xFDASBgNVBAMMCyouZ2l0aHViLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAuLBgDhov8bGGS2TsEZ+meb7oh/GIxbRJmxC7yq/qr75UDHhDf8p7
TkVbCyQp8bsj/Bmkx2xwSXZT0wkjZbJIe7Ycqgca4nka+Xpe5xb4pkrVOaPiDfdX
7+34CHZbUtqL0OYebi/5D5lLalLKNOGkySAz05foenfFAxAmQYJhR6KvxFY/dqI4
y7JwrnJ6Q8F+J6Ne1uP256UwcL0qlid6e/tA0ld3ryMSJ0I6xgtqjL26Le4/nxXu
YlekppVQr0OwrHa44Q7Z/1bsdFCGtR+WLNGVBeW3BWeTTp7yWjgfp49DWt48V9pI
elDGiDgVyJcsLOz4OQk2vRmNA1ZBZgck4wIDAQABo4ID0DCCA8wwHwYDVR0jBBgw
FoAUt2ui6qiqhIx56rTaD5iyxZV2ufQwHQYDVR0OBBYEFI0CHHVazcamQXhpKMP3
qqeYO9W7MHsGA1UdEQR0MHKCCyouZ2l0aHViLmlvgglnaXRodWIuaW+CDCouZ2l0
aHViLmNvbYIKZ2l0aHViLmNvbYIOd3d3LmdpdGh1Yi5jb22CFyouZ2l0aHVidXNl
cmNvbnRlbnQuY29tghVnaXRodWJ1c2VyY29udGVudC5jb20wDgYDVR0PAQH/BAQD
AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjCBjwYDVR0fBIGHMIGE
MECgPqA8hjpodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUTFNSU0FT
SEEyNTYyMDIwQ0ExLTQuY3JsMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2VydC5j
b20vRGlnaUNlcnRUTFNSU0FTSEEyNTYyMDIwQ0ExLTQuY3JsMD4GA1UdIAQ3MDUw
MwYGZ4EMAQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29t
L0NQUzB/BggrBgEFBQcBAQRzMHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
Z2ljZXJ0LmNvbTBJBggrBgEFBQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu
Y29tL0RpZ2lDZXJ0VExTUlNBU0hBMjU2MjAyMENBMS0xLmNydDAJBgNVHRMEAjAA
MIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdwB2/4g/Crb7lVHCYcz1h7o0tKTN
uyncaEIKn+ZnTFo6dAAAAYZ0gHV7AAAEAwBIMEYCIQCqfmfSO8MxeeVZ/fJzqqBB
p+VqeRDUOUBVGyTTOn43ewIhAJT0S27mmGUlpqNiDADP+Jo8C6kYHF+7U6TY74bH
XHAaAHYAc9meiRtMlnigIH1HneayxhzQUV5xGSqMa4AQesF3crUAAAGGdIB1agAA
BAMARzBFAiEAguB+XQVANBj2MPcJzbz+LBPrkDDOEO3op52jdHUSW3ICIF0fnYdW
qvdtmgQNSns13pAppdQWp4/f/jerNYskI7krAHUASLDja9qmRzQP5WoC+p0w6xxS
ActW3SyB2bu/qznYhHMAAAGGdIB1SgAABAMARjBEAiAT/wA2qGGHSKZqBAm84z6q
E+dGPQZ1aCMY52pFSfcw8QIgP/SciuZG02X2mBO/miDT2hCp4y5d2sc7FE5PThyC
pbMwDQYJKoZIhvcNAQELBQADggEBADekGxEin/yfyWcHj6qGE5/gCB1uDI1l+wN5
UMZ2ujCQoKQceRMHuVoYjZdMBXGK0CIXxhmiIosD9iyEcWxV3+KZQ2Xl17e3N0zG
yOXx2Kd7B13ruBxQpKOO8Ez4uGpyWb5DDoretV6Pnj9aQ2SCzODedvS+phIKBmi7
d+FM70tNZ6/2csdrG5xIU6d/7XYYXPD2xkwkU1dX4UKmPa7h9ZPyavopcgE+twbx
LxoOkcXsNb/12jOV3iQSDfXDI41AgtFc694KCOjlg+UKizpemE53T5/cq37OqChP
qnlPyb6PYIhua/kgbH84ltba1xEDQ9i4UYfOMiJNZEzEdSfQ498=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIHozCCBougAwIBAgIQBG+MjTu2U2CAH+MEm6l61jANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRUwEwYDVQQLEwxTZXJ2ZXIg
Q0EgMUIxDzANBgNVBAMTBkFtYXpvbjAeFw0yMjA0MDEwMDAwMDBaFw0yMzAzMzAy
MzU5NTlaMBsxGTAXBgNVBAMTEHMzLmFtYXpvbmF3cy5jb20wggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCjOMyeQT6RFlYmYfjj08zVKFFdJUIIBLNVC2Ij
chWl7zswWUmTtxj/cifpMx64WsGOSzwNZP+zpUX/0miTawNJOMi53taVwKt8PY03
Ud2Yr/RkRBGKsUTUYpIlSj4plJ6p2gdAqNMsI9dnYketpff5K9vmqQYh/qDeI4NQ
5+PsK22emJMgvjNaEOlOU95Xk5E02M9S2UB3MXlLO2G+YTTbsZW0QRQe6GDp/qXy
aGGHQbAL4Gthz8xm4C5+scBI0dMvjVcnW78IEO/YaB+zNsVkiMXV7OBtRIy5JJLB
EPc0QT2cl4h1ktcqVdKzQnCRfb53V9KA2gdAkmih47fAHQTfAgMBAAGjggS2MIIE
sjAfBgNVHSMEGDAWgBRZpGYGUqB7lZI8o5QHJ5Z0W/k90DAdBgNVHQ4EFgQURXXo
9PzHJg3VN6E4crTKJmllGZkwggHkBgNVHREEggHbMIIB14IQczMuYW1hem9uYXdz
LmNvbYISKi5zMy5hbWF6b25hd3MuY29tgiYqLnMzLmR1YWxzdGFjay51cy1lYXN0
LTEuYW1hem9uYXdzLmNvbYIkczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tghwqLnMzLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tghpzMy51cy1lYXN0
LTEuYW1hem9uYXdzLmNvbYIkKi5zMy1jb250cm9sLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiJzMy1jb250cm9sLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgi4qLnMz
LWNvbnRyb2wuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgixzMy1j
b250cm9sLmR1YWxzdGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIoKi5zMy1h
Y2Nlc3Nwb2ludC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIyKi5zMy1hY2Nlc3Nw
b2ludC5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CISouczMudXMt
ZWFzdC0xLnZwY2UuYW1hem9uYXdzLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0l
BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6
Ly9jcmwuc2NhMWIuYW1hem9udHJ1c3QuY29tL3NjYTFiLTEuY3JsMBMGA1UdIAQM
MAowCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkwZzAtBggrBgEFBQcwAYYhaHR0cDov
L29jc3Auc2NhMWIuYW1hem9udHJ1c3QuY29tMDYGCCsGAQUFBzAChipodHRwOi8v
Y3J0LnNjYTFiLmFtYXpvbnRydXN0LmNvbS9zY2ExYi5jcnQwDAYDVR0TAQH/BAIw
ADCCAX4GCisGAQQB1nkCBAIEggFuBIIBagFoAHYA6D7Q2j71BjUy51covIlryQPT
y9ERa+zraeF3fW0GvW4AAAF/5g3wggAABAMARzBFAiEAi/U3UhS0wBO3ENr60raR
eLOh2AG97uiMu5/XTpOitzkCIBS3J35I01Px5yxWB03Wbp5Q15rKflPnGdk4OxqA
YhwCAHUANc8ZG7+xbFe/D61MbULLu7YnICZR6j/hKu+oA8M71kwAAAF/5g3wxwAA
BAMARjBEAiBVLkL7vmHrvdgj4f9/EkWear0/EifCefzHAIkmZXROGgIgLM/cG6C/
oz8y8uDEZI6ijXsmlpPDe4azrAL9qMZ1LHcAdwCzc3cH4YRQ+GOG1gWp3BEJSnkt
sWcMC4fc8AMOeTalmgAAAX/mDfDrAAAEAwBIMEYCIQCc2ZP3gCZGJ8PQFrVZGk2K
4L4ebIhQQrfp9irYeyyBXgIhAPF45dE3vSvxkf/Qr1yba0eYQa+F0P92wV6Cpkqa
SHfrMA0GCSqGSIb3DQEBCwUAA4IBAQA4tbiyX/J+awY2zXRv7MhFFn5lg4qZLl59
DKoxuVQ3/os1UkOE3z7rNJVQZ9v1DrsadooHIQ69Uz5JUcdpVz57tSZs5H6Cf5EW
GaNgeeXvloME0G5KXAXBQ6NZrCaKzpOrGJRhKJGvrVgGhPqYAZ0fTXJ81vt5AICp
55pwVIMTLPR4ewD/l/OWuned8zXjBF2+WQ7Ex5E0VIv9MuFN/URXGWQ+I+2uGyfW
zlX9SweWNfLYYxTtY1duU7nzqnK6u2/N2IxO4Hkqehll3Ineyqdni4/CeMHOD06B
ox01tCIdK1Wz5I7K/F2jc6jinmZHYlX+oHaY5lAh9FLsQ+luBgHd
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIESTCCAzGgAwIBAgITBn+UV4WH6Kx33rJTMlu8mYtWDTANBgkqhkiG9w0BAQsF
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
b24gUm9vdCBDQSAxMB4XDTE1MTAyMjAwMDAwMFoXDTI1MTAxOTAwMDAwMFowRjEL
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEVMBMGA1UECxMMU2VydmVyIENB
IDFCMQ8wDQYDVQQDEwZBbWF6b24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDCThZn3c68asg3Wuw6MLAd5tES6BIoSMzoKcG5blPVo+sDORrMd4f2AbnZ
cMzPa43j4wNxhplty6aUKk4T1qe9BOwKFjwK6zmxxLVYo7bHViXsPlJ6qOMpFge5
blDP+18x+B26A0piiQOuPkfyDyeR4xQghfj66Yo19V+emU3nazfvpFA+ROz6WoVm
B5x+F2pV8xeKNR7u6azDdU5YVX1TawprmxRC1+WsAYmz6qP+z8ArDITC2FMVy2fw
0IjKOtEXc/VfmtTFch5+AfGYMGMqqvJ6LcXiAhqG5TI+Dr0RtM88k+8XUBCeQ8IG
KuANaL7TiItKZYxK1MMuTJtV9IblAgMBAAGjggE7MIIBNzASBgNVHRMBAf8ECDAG
AQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUWaRmBlKge5WSPKOUByeW
dFv5PdAwHwYDVR0jBBgwFoAUhBjMhTTsvAyUlC4IWZzHshBOCggwewYIKwYBBQUH
AQEEbzBtMC8GCCsGAQUFBzABhiNodHRwOi8vb2NzcC5yb290Y2ExLmFtYXpvbnRy
dXN0LmNvbTA6BggrBgEFBQcwAoYuaHR0cDovL2NydC5yb290Y2ExLmFtYXpvbnRy
dXN0LmNvbS9yb290Y2ExLmNlcjA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3Js
LnJvb3RjYTEuYW1hem9udHJ1c3QuY29tL3Jvb3RjYTEuY3JsMBMGA1UdIAQMMAow
CAYGZ4EMAQIBMA0GCSqGSIb3DQEBCwUAA4IBAQCFkr41u3nPo4FCHOTjY3NTOVI1
59Gt/a6ZiqyJEi+752+a1U5y6iAwYfmXss2lJwJFqMp2PphKg5625kXg8kP2CN5t
6G7bMQcT8C8xDZNtYTd7WPD8UZiRKAJPBXa30/AbwuZe0GaFEQ8ugcYQgSn+IGBI
8/LwhBNTZTUVEWuCUUBVV18YtbAiPq3yXqMB48Oz+ctBWuZSkbvkNodPLamkB2g1
upRyzQ7qDn1X8nn8N8V7YJ6y68AtkHcNSRAnpTitxBKjtKPISLMVCx7i4hncxHZS
yLyKQXhw2W2Xs0qLeC1etA+jTGDK4UfLeC0SF7FSi8o5LL21L8IzApar2pR/
MIIHljCCBn6gAwIBAgIQC65deUww9ISBucGLLymfiTANBgkqhkiG9w0BAQsFADA8
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g
UlNBIDIwNDggTTAxMB4XDTIyMTIwNjAwMDAwMFoXDTIzMTIwNTIzNTk1OVowGzEZ
MBcGA1UEAxMQczMuYW1hem9uYXdzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAOai9pH7hBruq8Z2Nh7PM3wTYkupXpQsfyV1aIoDE2Wb5Eq6j6lc
XstKcAHOSBZomYCqRVjB06hWhT/Vmi8jCIUKj/4ylgl/OUD1Fmp3zPzzp0xXo6W1
nMI8t66EnzHzMmUAFcaobb/+XGnsBM/JnOvFgcsnit5b0ohHSrrBkhqoPG9UV6s1
ihoIu/inxP/OZK3julKF9DQeQFik+aY5xHyR7NU/YFvgXLOjFtZHNV4M/37K+E8q
BG2Fc+23sn+6fRyhkgldIwbUcIgEVfbkRVWBP5zJVbYo7V3yOpauit3FIhTpVATO
50AdnLYIPa8HtkEt74uKoiPAvEsX1KLlbFECAwEAAaOCBLMwggSvMB8GA1UdIwQY
MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBQYwyLcdwl9dkli9Knk
oe8DnTMQQzCCAeQGA1UdEQSCAdswggHXghBzMy5hbWF6b25hd3MuY29tghIqLnMz
LmFtYXpvbmF3cy5jb22CJiouczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiRzMy5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CHCou
czMudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CGnMzLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiQqLnMzLWNvbnRyb2wudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CInMz
LWNvbnRyb2wudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLiouczMtY29udHJvbC5k
dWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLHMzLWNvbnRyb2wuZHVh
bHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgigqLnMzLWFjY2Vzc3BvaW50
LnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgjIqLnMzLWFjY2Vzc3BvaW50LmR1YWxz
dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIhKi5zMy51cy1lYXN0LTEudnBj
ZS5hbWF6b25hd3MuY29tMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF
BQcDAQYIKwYBBQUHAwIwOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5yMm0w
MS5hbWF6b250cnVzdC5jb20vcjJtMDEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIB
MHUGCCsGAQUFBwEBBGkwZzAtBggrBgEFBQcwAYYhaHR0cDovL29jc3AucjJtMDEu
YW1hem9udHJ1c3QuY29tMDYGCCsGAQUFBzAChipodHRwOi8vY3J0LnIybTAxLmFt
YXpvbnRydXN0LmNvbS9yMm0wMS5jZXIwDAYDVR0TAQH/BAIwADCCAX0GCisGAQQB
1nkCBAIEggFtBIIBaQFnAHYA6D7Q2j71BjUy51covIlryQPTy9ERa+zraeF3fW0G
vW4AAAGE6TiWGwAABAMARzBFAiEAm7EmdOlYh6ZqCbcL031TNYm1It2F9ZOECd9k
Efd5rR0CIARC57eeRuS5w2mHogwnKEokTsA8MiMDQs/ysF8NJAQhAHUAs3N3B+GE
UPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAGE6TiWJQAABAMARjBEAiAyQAFf
DUdoch036Wz41eG8ukNAIZUNSo2XqOFUhGTrMQIgFwfTZ+Wk3DeuitGUeqRLTLOI
/RDTjp6k9iwL5ScBYSwAdgC3Pvsk35xNunXyOcW6WPRsXfxCz3qfNcSeHQmBJe20
mQAAAYTpOJXyAAAEAwBHMEUCIEjQE4RdlG0zH2oWP1OSi24e25WyUx3ww3fgLRMH
MYWSAiEA795WZhOkmboCZi2wrStw4+nBQLtStAUYbbXGXhX1bWEwDQYJKoZIhvcN
AQELBQADggEBAJO/33aYKDV+t01OfLUvRGR6wzcfW1dHmddMkH2CU0I+XJO8+rNe
1txfh00UpBDxG/CP/rx+ulA72+XgiXURFC5NtKiyUXit58rMujj21UTmqf3/70DB
KdJmMoXTIdz93ttmV4JpF3Oo3Wp8jXcRRKAJdH0aSOH1Fx+fcVa8u56WxsA7khGG
/XJcCJomARKpf1W3x9tRAOq6hVkWKSC8V4pyhc6qvymcvgvPHysC+xrRD9RZ38Ui
gijCabOG5gYIxE8DCBtVksYq2vxqVod8w8Kp+zHrLYwQbAbrk8JRsdT75GJ/LZhl
XAauB98KOqLvlBtdpu8GaAvtpN95tRMkab8=
-----END CERTIFICATE-----

View File

@@ -1,40 +1,40 @@
-----BEGIN CERTIFICATE-----
MIIHFDCCBfygAwIBAgIQCLS/dX/bKN3zuMTJNXxaSTANBgkqhkiG9w0BAQsFADBP
MIIHEjCCBfqgAwIBAgIQBE1y13zdpwLdWmfyoju92TANBgkqhkiG9w0BAQsFADBP
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSkwJwYDVQQDEyBE
aWdpQ2VydCBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTAeFw0yMjA0MDcwMDAwMDBa
Fw0yMzA0MDcyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y
aWdpQ2VydCBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTAeFw0yMzAyMjEwMDAwMDBa
Fw0yNDAzMjAyMzU5NTlaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y
bmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRUwEwYDVQQKEwxHaXRIdWIsIElu
Yy4xFTATBgNVBAMMDCouZ2l0aHViLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBALyqZjatk2jnqiWmp6eusW70yJlreKz8mllyRSPxnIVeuwCHGzeQ
pGOOZkdRiBLcC2SWM3WgwQjBVBzqS1hWgoP5e6hzuXvGM3anlgJDE9dDUJfdC/Is
nzB4Q5Y4TU3FcRCUaK4GMoJGC0fu0fDbH927yKAnvdErG4u+jFSqIidwEaEfPWCC
o3xCyQLHTknXQ9aaDvU6GHNX0us6G+bjdErIwQtC56F0ke7biV0A/DWX5V+hVsVY
jY9JbYNx+KFjmUxLibccXzXs0pJ+a6Xa4OhhrFebPwS+SQA+gxTTvZotj4J5kf2l
nM9H+1whu6I5qPebhlTRTKpxdPm9V647Zj8CAwEAAaOCA9EwggPNMB8GA1UdIwQY
MBaAFLdrouqoqoSMeeq02g+YssWVdrn0MB0GA1UdDgQWBBRWmrM0shNZi0idiZiI
7l3ryIMwdDB7BgNVHREEdDByggwqLmdpdGh1Yi5jb22CDnd3dy5naXRodWIuY29t
gglnaXRodWIuaW+CCmdpdGh1Yi5jb22CCyouZ2l0aHViLmlvghVnaXRodWJ1c2Vy
Y29udGVudC5jb22CFyouZ2l0aHVidXNlcmNvbnRlbnQuY29tMA4GA1UdDwEB/wQE
AwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwgY8GA1UdHwSBhzCB
hDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTUlNB
U0hBMjU2MjAyMENBMS00LmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQu
Y29tL0RpZ2lDZXJ0VExTUlNBU0hBMjU2MjAyMENBMS00LmNybDA+BgNVHSAENzA1
MDMGBmeBDAECAjApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNv
bS9DUFMwfwYIKwYBBQUHAQEEczBxMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k
aWdpY2VydC5jb20wSQYIKwYBBQUHMAKGPWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0
LmNvbS9EaWdpQ2VydFRMU1JTQVNIQTI1NjIwMjBDQTEtMS5jcnQwCQYDVR0TBAIw
ADCCAX8GCisGAQQB1nkCBAIEggFvBIIBawFpAHYA6D7Q2j71BjUy51covIlryQPT
y9ERa+zraeF3fW0GvW4AAAGABfvdbAAABAMARzBFAiAGLk49aFP9ARwPXCa59WnI
f5jIU5eFmqR6/W3Zm38KiwIhAIp8FySKqbKk600uO4iPsS6TW8hJl67PprwXYMlr
o3wPAHcANc8ZG7+xbFe/D61MbULLu7YnICZR6j/hKu+oA8M71kwAAAGABfvdXQAA
BAMASDBGAiEAjFarHnzcbBvQ8//um0zVd4G3T5zbW4XSUIJSTc5JGo8CIQDaT5K8
pji9egTYSypP9XfRK+Z2wID3j43uuGjiKSOKyQB2ALNzdwfhhFD4Y4bWBancEQlK
eS2xZwwLh9zwAw55NqWaAAABgAX73YsAAAQDAEcwRQIhAO/PWksY7Zd7W5NJr3e4
xRkx8J6Qv7a33VA3tkm96k4WAiBshJWPE2BjKzuQ/KEfiKnvD4dDa3btkmcWlpiD
R8AvQDANBgkqhkiG9w0BAQsFAAOCAQEARtY8iVMqqBCXGZj2NRhpxA4eS2b/e/56
JhnRWGz3wxf0aRjbaZ2sUH3aHe1UDyg4jVPgnSLsGnBMmN5Rk32uiB/5v6/uRhCa
l26Yi9MYbeQpt0980MxT5hhv8bThRiNa77+oAOcrYMJEGIf2/9k0yoefblEZTR02
6UU6pkDhxjMtpyNRr+IdqQM/4lCM6nu8FZ/qaLltvta1Enq+jEwEObo/PoBoQJzJ
j7hcu7rkyPQIK1raQ9pK7uFJ2/FgtxIUuT+by06LnUp82VB7QxlniXO2R4XgDzWd
umlpkAFJQvZ+Sa2rSdjynrTDedjQIv3s1jH2Tvao5fR23tW2XAQhVg==
Yy4xFDASBgNVBAMMCyouZ2l0aHViLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAuLBgDhov8bGGS2TsEZ+meb7oh/GIxbRJmxC7yq/qr75UDHhDf8p7
TkVbCyQp8bsj/Bmkx2xwSXZT0wkjZbJIe7Ycqgca4nka+Xpe5xb4pkrVOaPiDfdX
7+34CHZbUtqL0OYebi/5D5lLalLKNOGkySAz05foenfFAxAmQYJhR6KvxFY/dqI4
y7JwrnJ6Q8F+J6Ne1uP256UwcL0qlid6e/tA0ld3ryMSJ0I6xgtqjL26Le4/nxXu
YlekppVQr0OwrHa44Q7Z/1bsdFCGtR+WLNGVBeW3BWeTTp7yWjgfp49DWt48V9pI
elDGiDgVyJcsLOz4OQk2vRmNA1ZBZgck4wIDAQABo4ID0DCCA8wwHwYDVR0jBBgw
FoAUt2ui6qiqhIx56rTaD5iyxZV2ufQwHQYDVR0OBBYEFI0CHHVazcamQXhpKMP3
qqeYO9W7MHsGA1UdEQR0MHKCCyouZ2l0aHViLmlvgglnaXRodWIuaW+CDCouZ2l0
aHViLmNvbYIKZ2l0aHViLmNvbYIOd3d3LmdpdGh1Yi5jb22CFyouZ2l0aHVidXNl
cmNvbnRlbnQuY29tghVnaXRodWJ1c2VyY29udGVudC5jb20wDgYDVR0PAQH/BAQD
AgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjCBjwYDVR0fBIGHMIGE
MECgPqA8hjpodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUTFNSU0FT
SEEyNTYyMDIwQ0ExLTQuY3JsMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2VydC5j
b20vRGlnaUNlcnRUTFNSU0FTSEEyNTYyMDIwQ0ExLTQuY3JsMD4GA1UdIAQ3MDUw
MwYGZ4EMAQICMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29t
L0NQUzB/BggrBgEFBQcBAQRzMHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
Z2ljZXJ0LmNvbTBJBggrBgEFBQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu
Y29tL0RpZ2lDZXJ0VExTUlNBU0hBMjU2MjAyMENBMS0xLmNydDAJBgNVHRMEAjAA
MIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdwB2/4g/Crb7lVHCYcz1h7o0tKTN
uyncaEIKn+ZnTFo6dAAAAYZ0gHV7AAAEAwBIMEYCIQCqfmfSO8MxeeVZ/fJzqqBB
p+VqeRDUOUBVGyTTOn43ewIhAJT0S27mmGUlpqNiDADP+Jo8C6kYHF+7U6TY74bH
XHAaAHYAc9meiRtMlnigIH1HneayxhzQUV5xGSqMa4AQesF3crUAAAGGdIB1agAA
BAMARzBFAiEAguB+XQVANBj2MPcJzbz+LBPrkDDOEO3op52jdHUSW3ICIF0fnYdW
qvdtmgQNSns13pAppdQWp4/f/jerNYskI7krAHUASLDja9qmRzQP5WoC+p0w6xxS
ActW3SyB2bu/qznYhHMAAAGGdIB1SgAABAMARjBEAiAT/wA2qGGHSKZqBAm84z6q
E+dGPQZ1aCMY52pFSfcw8QIgP/SciuZG02X2mBO/miDT2hCp4y5d2sc7FE5PThyC
pbMwDQYJKoZIhvcNAQELBQADggEBADekGxEin/yfyWcHj6qGE5/gCB1uDI1l+wN5
UMZ2ujCQoKQceRMHuVoYjZdMBXGK0CIXxhmiIosD9iyEcWxV3+KZQ2Xl17e3N0zG
yOXx2Kd7B13ruBxQpKOO8Ez4uGpyWb5DDoretV6Pnj9aQ2SCzODedvS+phIKBmi7
d+FM70tNZ6/2csdrG5xIU6d/7XYYXPD2xkwkU1dX4UKmPa7h9ZPyavopcgE+twbx
LxoOkcXsNb/12jOV3iQSDfXDI41AgtFc694KCOjlg+UKizpemE53T5/cq37OqChP
qnlPyb6PYIhua/kgbH84ltba1xEDQ9i4UYfOMiJNZEzEdSfQ498=
-----END CERTIFICATE-----

BIN
server_certs/r2m01.cer Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.1 Normal file

Binary file not shown.

BIN
server_certs/r2m01.cer.2 Normal file

Binary file not shown.

View File

@@ -1,43 +1,43 @@
-----BEGIN CERTIFICATE-----
MIIHozCCBougAwIBAgIQBG+MjTu2U2CAH+MEm6l61jANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRUwEwYDVQQLEwxTZXJ2ZXIg
Q0EgMUIxDzANBgNVBAMTBkFtYXpvbjAeFw0yMjA0MDEwMDAwMDBaFw0yMzAzMzAy
MzU5NTlaMBsxGTAXBgNVBAMTEHMzLmFtYXpvbmF3cy5jb20wggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQCjOMyeQT6RFlYmYfjj08zVKFFdJUIIBLNVC2Ij
chWl7zswWUmTtxj/cifpMx64WsGOSzwNZP+zpUX/0miTawNJOMi53taVwKt8PY03
Ud2Yr/RkRBGKsUTUYpIlSj4plJ6p2gdAqNMsI9dnYketpff5K9vmqQYh/qDeI4NQ
5+PsK22emJMgvjNaEOlOU95Xk5E02M9S2UB3MXlLO2G+YTTbsZW0QRQe6GDp/qXy
aGGHQbAL4Gthz8xm4C5+scBI0dMvjVcnW78IEO/YaB+zNsVkiMXV7OBtRIy5JJLB
EPc0QT2cl4h1ktcqVdKzQnCRfb53V9KA2gdAkmih47fAHQTfAgMBAAGjggS2MIIE
sjAfBgNVHSMEGDAWgBRZpGYGUqB7lZI8o5QHJ5Z0W/k90DAdBgNVHQ4EFgQURXXo
9PzHJg3VN6E4crTKJmllGZkwggHkBgNVHREEggHbMIIB14IQczMuYW1hem9uYXdz
LmNvbYISKi5zMy5hbWF6b25hd3MuY29tgiYqLnMzLmR1YWxzdGFjay51cy1lYXN0
LTEuYW1hem9uYXdzLmNvbYIkczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tghwqLnMzLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tghpzMy51cy1lYXN0
LTEuYW1hem9uYXdzLmNvbYIkKi5zMy1jb250cm9sLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiJzMy1jb250cm9sLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgi4qLnMz
LWNvbnRyb2wuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgixzMy1j
b250cm9sLmR1YWxzdGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIoKi5zMy1h
Y2Nlc3Nwb2ludC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIyKi5zMy1hY2Nlc3Nw
b2ludC5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CISouczMudXMt
ZWFzdC0xLnZwY2UuYW1hem9uYXdzLmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0l
BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMD0GA1UdHwQ2MDQwMqAwoC6GLGh0dHA6
Ly9jcmwuc2NhMWIuYW1hem9udHJ1c3QuY29tL3NjYTFiLTEuY3JsMBMGA1UdIAQM
MAowCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkwZzAtBggrBgEFBQcwAYYhaHR0cDov
L29jc3Auc2NhMWIuYW1hem9udHJ1c3QuY29tMDYGCCsGAQUFBzAChipodHRwOi8v
Y3J0LnNjYTFiLmFtYXpvbnRydXN0LmNvbS9zY2ExYi5jcnQwDAYDVR0TAQH/BAIw
ADCCAX4GCisGAQQB1nkCBAIEggFuBIIBagFoAHYA6D7Q2j71BjUy51covIlryQPT
y9ERa+zraeF3fW0GvW4AAAF/5g3wggAABAMARzBFAiEAi/U3UhS0wBO3ENr60raR
eLOh2AG97uiMu5/XTpOitzkCIBS3J35I01Px5yxWB03Wbp5Q15rKflPnGdk4OxqA
YhwCAHUANc8ZG7+xbFe/D61MbULLu7YnICZR6j/hKu+oA8M71kwAAAF/5g3wxwAA
BAMARjBEAiBVLkL7vmHrvdgj4f9/EkWear0/EifCefzHAIkmZXROGgIgLM/cG6C/
oz8y8uDEZI6ijXsmlpPDe4azrAL9qMZ1LHcAdwCzc3cH4YRQ+GOG1gWp3BEJSnkt
sWcMC4fc8AMOeTalmgAAAX/mDfDrAAAEAwBIMEYCIQCc2ZP3gCZGJ8PQFrVZGk2K
4L4ebIhQQrfp9irYeyyBXgIhAPF45dE3vSvxkf/Qr1yba0eYQa+F0P92wV6Cpkqa
SHfrMA0GCSqGSIb3DQEBCwUAA4IBAQA4tbiyX/J+awY2zXRv7MhFFn5lg4qZLl59
DKoxuVQ3/os1UkOE3z7rNJVQZ9v1DrsadooHIQ69Uz5JUcdpVz57tSZs5H6Cf5EW
GaNgeeXvloME0G5KXAXBQ6NZrCaKzpOrGJRhKJGvrVgGhPqYAZ0fTXJ81vt5AICp
55pwVIMTLPR4ewD/l/OWuned8zXjBF2+WQ7Ex5E0VIv9MuFN/URXGWQ+I+2uGyfW
zlX9SweWNfLYYxTtY1duU7nzqnK6u2/N2IxO4Hkqehll3Ineyqdni4/CeMHOD06B
ox01tCIdK1Wz5I7K/F2jc6jinmZHYlX+oHaY5lAh9FLsQ+luBgHd
MIIHljCCBn6gAwIBAgIQC65deUww9ISBucGLLymfiTANBgkqhkiG9w0BAQsFADA8
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g
UlNBIDIwNDggTTAxMB4XDTIyMTIwNjAwMDAwMFoXDTIzMTIwNTIzNTk1OVowGzEZ
MBcGA1UEAxMQczMuYW1hem9uYXdzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAOai9pH7hBruq8Z2Nh7PM3wTYkupXpQsfyV1aIoDE2Wb5Eq6j6lc
XstKcAHOSBZomYCqRVjB06hWhT/Vmi8jCIUKj/4ylgl/OUD1Fmp3zPzzp0xXo6W1
nMI8t66EnzHzMmUAFcaobb/+XGnsBM/JnOvFgcsnit5b0ohHSrrBkhqoPG9UV6s1
ihoIu/inxP/OZK3julKF9DQeQFik+aY5xHyR7NU/YFvgXLOjFtZHNV4M/37K+E8q
BG2Fc+23sn+6fRyhkgldIwbUcIgEVfbkRVWBP5zJVbYo7V3yOpauit3FIhTpVATO
50AdnLYIPa8HtkEt74uKoiPAvEsX1KLlbFECAwEAAaOCBLMwggSvMB8GA1UdIwQY
MBaAFIG4DmOKiRIY5fo7O1CVn+blkBOFMB0GA1UdDgQWBBQYwyLcdwl9dkli9Knk
oe8DnTMQQzCCAeQGA1UdEQSCAdswggHXghBzMy5hbWF6b25hd3MuY29tghIqLnMz
LmFtYXpvbmF3cy5jb22CJiouczMuZHVhbHN0YWNrLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiRzMy5kdWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CHCou
czMudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CGnMzLnVzLWVhc3QtMS5hbWF6b25h
d3MuY29tgiQqLnMzLWNvbnRyb2wudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CInMz
LWNvbnRyb2wudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLiouczMtY29udHJvbC5k
dWFsc3RhY2sudXMtZWFzdC0xLmFtYXpvbmF3cy5jb22CLHMzLWNvbnRyb2wuZHVh
bHN0YWNrLnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgigqLnMzLWFjY2Vzc3BvaW50
LnVzLWVhc3QtMS5hbWF6b25hd3MuY29tgjIqLnMzLWFjY2Vzc3BvaW50LmR1YWxz
dGFjay51cy1lYXN0LTEuYW1hem9uYXdzLmNvbYIhKi5zMy51cy1lYXN0LTEudnBj
ZS5hbWF6b25hd3MuY29tMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEF
BQcDAQYIKwYBBQUHAwIwOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5yMm0w
MS5hbWF6b250cnVzdC5jb20vcjJtMDEuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIB
MHUGCCsGAQUFBwEBBGkwZzAtBggrBgEFBQcwAYYhaHR0cDovL29jc3AucjJtMDEu
YW1hem9udHJ1c3QuY29tMDYGCCsGAQUFBzAChipodHRwOi8vY3J0LnIybTAxLmFt
YXpvbnRydXN0LmNvbS9yMm0wMS5jZXIwDAYDVR0TAQH/BAIwADCCAX0GCisGAQQB
1nkCBAIEggFtBIIBaQFnAHYA6D7Q2j71BjUy51covIlryQPTy9ERa+zraeF3fW0G
vW4AAAGE6TiWGwAABAMARzBFAiEAm7EmdOlYh6ZqCbcL031TNYm1It2F9ZOECd9k
Efd5rR0CIARC57eeRuS5w2mHogwnKEokTsA8MiMDQs/ysF8NJAQhAHUAs3N3B+GE
UPhjhtYFqdwRCUp5LbFnDAuH3PADDnk2pZoAAAGE6TiWJQAABAMARjBEAiAyQAFf
DUdoch036Wz41eG8ukNAIZUNSo2XqOFUhGTrMQIgFwfTZ+Wk3DeuitGUeqRLTLOI
/RDTjp6k9iwL5ScBYSwAdgC3Pvsk35xNunXyOcW6WPRsXfxCz3qfNcSeHQmBJe20
mQAAAYTpOJXyAAAEAwBHMEUCIEjQE4RdlG0zH2oWP1OSi24e25WyUx3ww3fgLRMH
MYWSAiEA795WZhOkmboCZi2wrStw4+nBQLtStAUYbbXGXhX1bWEwDQYJKoZIhvcN
AQELBQADggEBAJO/33aYKDV+t01OfLUvRGR6wzcfW1dHmddMkH2CU0I+XJO8+rNe
1txfh00UpBDxG/CP/rx+ulA72+XgiXURFC5NtKiyUXit58rMujj21UTmqf3/70DB
KdJmMoXTIdz93ttmV4JpF3Oo3Wp8jXcRRKAJdH0aSOH1Fx+fcVa8u56WxsA7khGG
/XJcCJomARKpf1W3x9tRAOq6hVkWKSC8V4pyhc6qvymcvgvPHysC+xrRD9RZ38Ui
gijCabOG5gYIxE8DCBtVksYq2vxqVod8w8Kp+zHrLYwQbAbrk8JRsdT75GJ/LZhl
XAauB98KOqLvlBtdpu8GaAvtpN95tRMkab8=
-----END CERTIFICATE-----

Binary file not shown.

View File

@@ -1,25 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIESTCCAzGgAwIBAgITBn+UV4WH6Kx33rJTMlu8mYtWDTANBgkqhkiG9w0BAQsF
ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
b24gUm9vdCBDQSAxMB4XDTE1MTAyMjAwMDAwMFoXDTI1MTAxOTAwMDAwMFowRjEL
MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEVMBMGA1UECxMMU2VydmVyIENB
IDFCMQ8wDQYDVQQDEwZBbWF6b24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDCThZn3c68asg3Wuw6MLAd5tES6BIoSMzoKcG5blPVo+sDORrMd4f2AbnZ
cMzPa43j4wNxhplty6aUKk4T1qe9BOwKFjwK6zmxxLVYo7bHViXsPlJ6qOMpFge5
blDP+18x+B26A0piiQOuPkfyDyeR4xQghfj66Yo19V+emU3nazfvpFA+ROz6WoVm
B5x+F2pV8xeKNR7u6azDdU5YVX1TawprmxRC1+WsAYmz6qP+z8ArDITC2FMVy2fw
0IjKOtEXc/VfmtTFch5+AfGYMGMqqvJ6LcXiAhqG5TI+Dr0RtM88k+8XUBCeQ8IG
KuANaL7TiItKZYxK1MMuTJtV9IblAgMBAAGjggE7MIIBNzASBgNVHRMBAf8ECDAG
AQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUWaRmBlKge5WSPKOUByeW
dFv5PdAwHwYDVR0jBBgwFoAUhBjMhTTsvAyUlC4IWZzHshBOCggwewYIKwYBBQUH
AQEEbzBtMC8GCCsGAQUFBzABhiNodHRwOi8vb2NzcC5yb290Y2ExLmFtYXpvbnRy
dXN0LmNvbTA6BggrBgEFBQcwAoYuaHR0cDovL2NydC5yb290Y2ExLmFtYXpvbnRy
dXN0LmNvbS9yb290Y2ExLmNlcjA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3Js
LnJvb3RjYTEuYW1hem9udHJ1c3QuY29tL3Jvb3RjYTEuY3JsMBMGA1UdIAQMMAow
CAYGZ4EMAQIBMA0GCSqGSIb3DQEBCwUAA4IBAQCFkr41u3nPo4FCHOTjY3NTOVI1
59Gt/a6ZiqyJEi+752+a1U5y6iAwYfmXss2lJwJFqMp2PphKg5625kXg8kP2CN5t
6G7bMQcT8C8xDZNtYTd7WPD8UZiRKAJPBXa30/AbwuZe0GaFEQ8ugcYQgSn+IGBI
8/LwhBNTZTUVEWuCUUBVV18YtbAiPq3yXqMB48Oz+ctBWuZSkbvkNodPLamkB2g1
upRyzQ7qDn1X8nn8N8V7YJ6y68AtkHcNSRAnpTitxBKjtKPISLMVCx7i4hncxHZS
yLyKQXhw2W2Xs0qLeC1etA+jTGDK4UfLeC0SF7FSi8o5LL21L8IzApar2pR/
-----END CERTIFICATE-----

View File

@@ -1,3 +1,13 @@
# Check if the required dependencies are installed
find_package(Python3 COMPONENTS Interpreter)
if(Python3_Interpreter_FOUND)
execute_process(COMMAND pip3 install protobuf grpcio-tools)
else()
message(FATAL_ERROR "Python3 interpreter not found. Please install Python3 before building the project.")
endif()
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
function(___register_flash partition_name sub_type)