pax_global_header00006660000000000000000000000064141653442020014513gustar00rootroot0000000000000052 comment=874e4f97d979fbe9b396c1997730a1a2d6797964 squeezelite-1.9.9-1395+git20220104.874e4f9/000077500000000000000000000000001416534420200170745ustar00rootroot00000000000000squeezelite-1.9.9-1395+git20220104.874e4f9/.gitignore000066400000000000000000000001141416534420200210600ustar00rootroot00000000000000.vscode/ *.o squeezelite squeezelite-pulse tools/alsacap tools/find_servers squeezelite-1.9.9-1395+git20220104.874e4f9/LICENSE.txt000066400000000000000000000056601416534420200207260ustar00rootroot00000000000000Squeezelite - lightweight headless squeezebox emulator (c) Adrian Smith 2012-2015, triode1@btinternet.com Ralph Irving 2015-2021, ralph_irving@hotmail.com Released under GPLv3 license: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additional permission under GNU GPL version 3 section 7 If you modify this program, or any covered work, by linking or combining it with OpenSSL (or a modified version of that library), containing parts covered by the terms of The OpenSSL Project, the licensors of this program grant you additional permission to convey the resulting work. {Corresponding source for a non-source form of such a combination shall include the source code for the parts of OpenSSL used as well as that of the covered work.} --------------------------------------------------------------------- If built with DSD support, this software also includes code subject to the following license: Copyright 2009, 2011 Sebastian Gesemann. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY SEBASTIAN GESEMANN ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN GESEMANN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Sebastian Gesemann. squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile000066400000000000000000000102311416534420200205310ustar00rootroot00000000000000#Cross compile support - create a Makefile which defines these three variables and then includes this Makefile... CFLAGS ?= -Wall -fPIC -O2 CFLAGS += -fcommon LDADD ?= -lpthread -lm -lrt EXECUTABLE ?= squeezelite # passing one or more of these in $(OPTS) enables optional feature inclusion OPT_DSD = -DDSD OPT_FF = -DFFMPEG OPT_ALAC = -DALAC OPT_LINKALL = -DLINKALL OPT_RESAMPLE= -DRESAMPLE OPT_VIS = -DVISEXPORT OPT_IR = -DIR OPT_GPIO = -DGPIO OPT_RPI = -DRPI OPT_NO_FAAD = -DNO_FAAD OPT_SSL = -DUSE_SSL OPT_NOSSLSYM= -DNO_SSLSYM OPT_OPUS = -DOPUS OPT_PORTAUDIO = -DPORTAUDIO OPT_PULSEAUDIO = -DPULSEAUDIO SOURCES = \ main.c slimproto.c buffer.c stream.c utils.c \ output.c output_alsa.c output_pa.c output_stdout.c output_pack.c output_pulse.c decode.c \ flac.c pcm.c mad.c vorbis.c mpg.c SOURCES_DSD = dsd.c dop.c dsd2pcm/dsd2pcm.c SOURCES_FF = ffmpeg.c SOURCES_ALAC = alac.c alac_wrapper.cpp SOURCES_RESAMPLE = process.c resample.c SOURCES_VIS = output_vis.c SOURCES_IR = ir.c SOURCES_GPIO = gpio.c SOURCES_RPI = minimal_gpio.c SOURCES_FAAD = faad.c SOURCES_SSL = sslsym.c SOURCES_OPUS = opus.c LINK_LINUX = -ldl LINK_ALSA = -lasound LINK_PORTAUDIO = -lportaudio LINK_PULSEAUDIO = -lpulse LINK_SSL = -lssl -lcrypto LINK_ALAC = -lalac LINKALL = -lmad -lmpg123 -lFLAC -lvorbisfile -lvorbis -logg LINKALL_FF = -lavformat -lavcodec -lavutil LINKALL_RESAMPLE = -lsoxr LINKALL_IR = -llirc_client LINKALL_FAAD = -lfaad LINKALL_OPUS = -lopusfile -lopus DEPS = squeezelite.h slimproto.h UNAME = $(shell uname -s) # add optional sources ifneq (,$(findstring $(OPT_DSD), $(OPTS))) SOURCES += $(SOURCES_DSD) endif ifneq (,$(findstring $(OPT_FF), $(OPTS))) SOURCES += $(SOURCES_FF) endif ifneq (,$(findstring $(OPT_ALAC), $(OPTS))) SOURCES += $(SOURCES_ALAC) DEPS += alac_wrapper.h endif ifneq (,$(findstring $(OPT_OPUS), $(OPTS))) SOURCES += $(SOURCES_OPUS) endif ifneq (,$(findstring $(OPT_RESAMPLE), $(OPTS))) SOURCES += $(SOURCES_RESAMPLE) endif ifneq (,$(findstring $(OPT_VIS), $(OPTS))) SOURCES += $(SOURCES_VIS) endif ifneq (,$(findstring $(OPT_IR), $(OPTS))) SOURCES += $(SOURCES_IR) endif ifneq (,$(findstring $(OPT_GPIO), $(OPTS))) SOURCES += $(SOURCES_GPIO) endif ifneq (,$(findstring $(OPT_RPI), $(OPTS))) SOURCES += $(SOURCES_RPI) endif # ensure GPIO is enabled with RPI ifneq (,$(findstring $(OPT_RPI), $(OPTS))) ifeq (,$(findstring $(SOURCES_GPIO), $(SOURCES))) CFLAGS += $(OPT_GPIO) SOURCES += $(SOURCES_GPIO) endif endif ifeq (,$(findstring $(OPT_NO_FAAD), $(OPTS))) SOURCES += $(SOURCES_FAAD) endif ifneq (,$(findstring $(OPT_SSL), $(OPTS))) SOURCES += $(SOURCES_SSL) endif # add optional link options ifneq (,$(findstring $(OPT_LINKALL), $(OPTS))) LDADD += $(LINKALL) ifneq (,$(findstring $(OPT_FF), $(OPTS))) LDADD += $(LINKALL_FF) endif ifneq (,$(findstring $(OPT_OPUS), $(OPTS))) LDADD += $(LINKALL_OPUS) endif ifneq (,$(findstring $(OPT_RESAMPLE), $(OPTS))) LDADD += $(LINKALL_RESAMPLE) endif ifneq (,$(findstring $(OPT_IR), $(OPTS))) LDADD += $(LINKALL_IR) endif ifeq (,$(findstring $(OPT_NO_FAAD), $(OPTS))) LDADD += $(LINKALL_FAAD) endif ifneq (,$(findstring $(OPT_SSL), $(OPTS))) LDADD += $(LINK_SSL) endif else # if not LINKALL and linux add LINK_LINUX ifeq ($(UNAME), Linux) LDADD += $(LINK_LINUX) endif ifneq (,$(findstring $(OPT_NOSSLSYM), $(OPTS))) LDADD += $(LINK_SSL) endif endif ifneq (,$(findstring $(OPT_PULSEAUDIO), $(OPTS))) LDADD += $(LINK_PULSEAUDIO) else ifneq (,$(findstring $(OPT_PORTAUDIO), $(OPTS))) LDADD += $(LINK_PORTAUDIO) else LDADD += $(LINK_ALSA) endif ifneq (,$(findstring $(OPT_ALAC), $(OPTS))) LDADD += $(LINK_ALAC) endif OBJECTS = $(addsuffix .o,$(basename $(SOURCES))) all: $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) ifneq (,$(findstring $(OPT_ALAC), $(OPTS))) $(CXX) $(OBJECTS) $(LDFLAGS) $(LDADD) -o $@ else $(CC) $(OBJECTS) $(LDFLAGS) $(LDADD) -o $@ endif $(OBJECTS): $(DEPS) .cpp.o: $(CXX) $(CXXFLAGS) $(CFLAGS) $(CPPFLAGS) $(OPTS) -Wno-multichar $< -c -o $@ .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) $(OPTS) $< -c -o $@ clean: rm -f $(OBJECTS) $(EXECUTABLE) print-%: @echo $* = $($*) squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.armel000066400000000000000000000013711416534420200216350ustar00rootroot00000000000000# Makefile armel OPTS = -DRESAMPLE -DFFMPEG -DVISEXPORT -DDSD -DIR -DGPIO -I/usr/local/include CFLAGS ?= -s -Wall -fPIC -O3 -march=armv5te $(OPTS) LDFLAGS ?= -s -lasound -lpthread -ldl -lrt -Wl,-rpath,/usr/local/lib EXECUTABLE ?= squeezelite SOURCES = main.c slimproto.c utils.c buffer.c stream.c decode.c flac.c pcm.c mad.c vorbis.c output_alsa.c output.c output_pa.c output_pack.c output_stdout.c output_vis.c dop.c dsd.c dsd2pcm/dsd2pcm.c faad.c mpg.c resample.c process.c ffmpeg.c ir.c gpio.c DEPS = squeezelite.h slimproto.h dsd2pcm/dsd2pcm.h OBJECTS = $(SOURCES:.c=.o) all: $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(OBJECTS) $(LDFLAGS) -o $@ $(OBJECTS): $(DEPS) .c.o: $(CC) $(CFLAGS) $< -c -o $@ clean: rm -f $(OBJECTS) $(EXECUTABLE) squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.freebsd000066400000000000000000000002731416534420200221470ustar00rootroot00000000000000OPTS = -DFFMPEG -DRESAMPLE -DGPIO -DUSE_SSL CPPFLAGS = -I/usr/local/include -I/usr/local/include/portaudio2 LDFLAGS = -L/usr/local/lib /usr/local/lib/libportaudio.a -lm include Makefile squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.i386000066400000000000000000000012511416534420200212230ustar00rootroot00000000000000# OSX 10.7 Intel 32-bit OPTS = -DPORTAUDIO -DALAC -DOPUS -DRESAMPLE -DLINKALL -DVISEXPORT -DDSD -DUSE_SSL -I./include -I./include/opus -I./include/alac -I./include -O3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -arch i386 -mmacosx-version-min=10.7 LDFLAGS = -Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -arch i386 -mmacosx-version-min=10.7 -L./lib LDADD = -lportaudio -lpthread -ldl -lm -framework CoreVideo -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon EXECUTABLE = squeezelite-i386 include Makefile squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.m1000066400000000000000000000012401416534420200210450ustar00rootroot00000000000000# OSX 11.0+ arm64 only OPTS = -DPORTAUDIO -DALAC -DOPUS -DRESAMPLE -DLINKALL -DVISEXPORT -DDSD -DUSE_SSL -I./includem1 -I./includem1/opus -I./includem1/alac -arch arm64 -O3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -mmacosx-version-min=11.0 LDFLAGS = -arch arm64 -Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -mmacosx-version-min=11.0 -L./libm1 LDADD = -lportaudio -lpthread -ldl -lm -framework CoreVideo -framework VideoDecodeAcceleration -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon include Makefile squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.osx000066400000000000000000000005201416534420200213410ustar00rootroot00000000000000# OSX build - adjust -I to point to header files for codecs and portaudio CFLAGS = -arch x86_64 -arch i386 -Wall -fPIC -O2 -I./include $(OPTS) LDFLAGS = -arch x86_64 -arch i386 -lpthread libportaudio.a -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon EXECUTABLE ?= squeezelite-osx include Makefile squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.pa000066400000000000000000000002321416534420200211300ustar00rootroot00000000000000# Make with portaudio rather than direct alsa OPTS += -DPORTAUDIO LDFLAGS = -lportaudio -lpthread -ldl -lrt EXECUTABLE = squeezelite-pa include Makefile squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.paoss000066400000000000000000000014011416534420200216540ustar00rootroot00000000000000# Cross compile support - create a Makefile which defines these three variables and then includes this Makefile... CFLAGS ?= -Wall -DPORTAUDIO -DPA18API -fPIC -O3 -I`pwd`/include $(OPTS) LDFLAGS ?= -lpthread -lm -ldl -lrt -L`pwd`/lib -lportaudio EXECUTABLE ?= squeezelite-oss SOURCES = main.c slimproto.c buffer.c stream.c utils.c output.c output_alsa.c output_pa.c output_stdout.c output_pack.c output_vis.c decode.c flac.c pcm.c mad.c vorbis.c faad.c mpg.c dsd.c dop.c dsd2pcm/dsd2pcm.c ffmpeg.c process.c resample.c ir.c DEPS = squeezelite.h slimproto.h OBJECTS = $(SOURCES:.c=.o) all: $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(OBJECTS) $(LDFLAGS) -o $@ $(OBJECTS): $(DEPS) .c.o: $(CC) $(CFLAGS) $< -c -o $@ clean: rm -f $(OBJECTS) $(EXECUTABLE) squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.ppc000066400000000000000000000016351416534420200213220ustar00rootroot00000000000000# OSX 10.4 PPC 32-bit CC=powerpc-apple-darwin10-gcc CXX=powerpc-apple-darwin10-gcc CFLAGS ?= -s -Wall -fPIC -DOSXPPC -DLINKALL -O2 -I./include -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mcpu=G3 -arch ppc LDFLAGS ?= -Wl,-syslibroot,/Developer/SDKs/MacOSX10.4u.sdk -arch ppc -mmacosx-version-min=10.3 -L./lib -lFLAC -lvorbisfile -lvorbis -logg -lmad -lfaad -lmpg123 -lpthread -ldl -lm -lportaudio -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon EXECUTABLE ?= squeezelite-ppc SOURCES = main.c slimproto.c buffer.c stream.c utils.c output.c output_alsa.c output_pa.c output_stdout.c output_pack.c decode.c flac.c pcm.c mad.c vorbis.c faad.c mpg.c DEPS = squeezelite.h slimproto.h OBJECTS = $(SOURCES:.c=.o) all: $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(OBJECTS) $(LDFLAGS) -o $@ $(OBJECTS): $(DEPS) .c.o: $(CC) $(CFLAGS) $< -c -o $@ clean: rm -f $(OBJECTS) $(EXECUTABLE) squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.ppc64000066400000000000000000000017001416534420200214650ustar00rootroot00000000000000# OSX 10.3 PPC 64-bit CC=powerpc-apple-darwin10-gcc CXX=powerpc-apple-darwin10-gcc CFLAGS ?= -s -Wall -fPIC -DOSXPPC -DLINKALL -O2 -I./include64 -isysroot /Developer/SDKs/MacOSX10.5.sdk -m64 -arch ppc64 -mmacosx-version-min=10.3 LDFLAGS ?= -m64 -Wl,-syslibroot,/Developer/SDKs/MacOSX10.5.sdk -arch ppc64 -mmacosx-version-min=10.3 -L./lib64 -lFLAC -lvorbisfile -lvorbis -logg -lmad -lfaad -lmpg123 -lpthread -ldl -lm -lportaudio -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon EXECUTABLE ?= squeezelite-ppc64 SOURCES = main.c slimproto.c buffer.c stream.c utils.c output.c output_alsa.c output_pa.c output_stdout.c output_pack.c decode.c flac.c pcm.c mad.c vorbis.c faad.c mpg.c DEPS = squeezelite.h slimproto.h OBJECTS = $(SOURCES:.c=.o) all: $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(OBJECTS) $(LDFLAGS) -o $@ $(OBJECTS): $(DEPS) .c.o: $(CC) $(CFLAGS) $< -c -o $@ clean: rm -f $(OBJECTS) $(EXECUTABLE) squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.pulse000066400000000000000000000002301416534420200216560ustar00rootroot00000000000000# Make with PulseAudio rather than direct alsa OPTS += -DPULSEAUDIO LDADD = -lrt -lpulse -lpthread -lm EXECUTABLE = squeezelite-pulse include Makefile squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.resample000066400000000000000000000000441416534420200223410ustar00rootroot00000000000000OPTS = -DRESAMPLE include Makefile squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.rpi000066400000000000000000000004041416534420200213230ustar00rootroot00000000000000OPTS = -DOPUS -DALAC -DRESAMPLE -DVISEXPORT -DDSD -DIR -DGPIO -DRPI -DUSE_SSL -DLINKALL -I./include -I./include/opus -I./include/alac -I/usr/local/include -s -march=armv6 -mfloat-abi=hard -mfpu=vfp LDFLAGS=-L./lib -L/usr/local/lib -s -lgomp include Makefile squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.sun000066400000000000000000000013451416534420200213430ustar00rootroot00000000000000# Solaris SPARC portaudio v18 api CC = gcc CPP = cpp CFLAGS ?= -fPIC -Wall -O3 -DRESAMPLE -DGPIO -I`pwd`/include -s LDFLAGS ?= -lpthread -lsocket -lnsl -ldl -lrt -lm -L`pwd`/lib -lportaudio -R/opt/squeezelite/lib -s EXECUTABLE ?= squeezelite-sun SOURCES = main.c slimproto.c utils.c buffer.c stream.c decode.c flac.c pcm.c mad.c vorbis.c output_alsa.c output.c output_pa.c output_pack.c output_stdout.c output_vis.c daemonize.c faad.c mpg.c resample.c process.c gpio.c ffmpeg.c DEPS = squeezelite.h slimproto.h dsd2pcm/dsd2pcm.h OBJECTS = $(SOURCES:.c=.o) all: $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(OBJECTS) $(LDFLAGS) -o $@ $(OBJECTS): $(DEPS) .c.o: $(CC) $(CFLAGS) $< -c -o $@ clean: rm -f $(OBJECTS) $(EXECUTABLE) squeezelite-1.9.9-1395+git20220104.874e4f9/Makefile.x86_64000066400000000000000000000012511416534420200214700ustar00rootroot00000000000000# OSX 10.7+ 64-bit only OPTS = -DPORTAUDIO -DALAC -DOPUS -DRESAMPLE -DLINKALL -DVISEXPORT -DDSD -DUSE_SSL -I./include64 -I./include64/opus -I./include64/alac -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -arch x86_64 -mmacosx-version-min=10.7 LDFLAGS = -Wl,-syslibroot,/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk -arch x86_64 -mmacosx-version-min=10.7 -L./lib64 LDADD = -lportaudio -lpthread -ldl -lm -framework CoreVideo -framework VideoDecodeAcceleration -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon include Makefile squeezelite-1.9.9-1395+git20220104.874e4f9/README.md000066400000000000000000000037771416534420200203710ustar00rootroot00000000000000Squeezelite v1.9.x, Copyright 2012-2015 Adrian Smith, 2015-2021 Ralph Irving.

See the squeezelite manpage for usage details.
https://ralph-irving.github.io/squeezelite.html

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see .

Additional permission under GNU GPL version 3 section 7

If you modify this program, or any covered work, by linking or combining it with OpenSSL (or a modified version of that library), containing parts covered by the terms of The OpenSSL Project, the licensors of this program grant you additional permission to convey the resulting work. {Corresponding source for a non-source form of such a combination shall include the source code for the parts of OpenSSL used as well as that of the covered work.}

Contains dsd2pcm library Copyright 2009, 2011 Sebastian Gesemann which
is subject to its own license.

Contains the Daphile Project full dsd patch Copyright 2013-2017 Daphile,
which is subject to its own license.

Option to allow server side upsampling for PCM streams (-W) from
squeezelite-R2 (c) Marco Curti 2015, marcoc1712@gmail.com.

RaspberryPi minimal GPIO Interface
http://abyz.me.uk/rpi/pigpio/examples.html#Misc_minimal_gpio.

This software uses libraries from the FFmpeg project under
the LGPLv2.1 and its source can be downloaded from
https://sourceforge.net/projects/lmsclients/files/source/
squeezelite-1.9.9-1395+git20220104.874e4f9/alac.c000066400000000000000000000360641416534420200201510ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * (c) Philippe, philippe_44@outlook.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #if ALAC #include "alac_wrapper.h" #if BYTES_PER_FRAME == 4 #define ALIGN8(n) (n << 8) #define ALIGN16(n) (n) #define ALIGN24(n) (n >> 8) #define ALIGN32(n) (n >> 16) #else #define ALIGN8(n) (n << 24) #define ALIGN16(n) (n << 16) #define ALIGN24(n) (n << 8) #define ALIGN32(n) (n) #endif #define BLOCK_SIZE (4096 * BYTES_PER_FRAME) #define MIN_READ BLOCK_SIZE #define MIN_SPACE (MIN_READ * 4) struct chunk_table { u32_t sample, offset; }; struct alac { void *decoder; u8_t *writebuf; // following used for mp4 only u32_t consume; u32_t pos; u32_t sample; u32_t nextchunk; void *stsc; u32_t skip; u64_t samples; u64_t sttssamples; bool empty; struct chunk_table *chunkinfo; u32_t *block_size, default_block_size, block_index; unsigned sample_rate; unsigned char channels, sample_size; unsigned trak, play; }; static struct alac *l; extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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 // read mp4 header to extract config data static int read_mp4_header(void) { size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); char type[5]; u32_t len; while (bytes >= 8) { // count trak to find the first playable one u32_t consume; len = unpackN((u32_t *)streambuf->readp); memcpy(type, streambuf->readp + 4, 4); type[4] = '\0'; if (!strcmp(type, "moov")) { l->trak = 0; l->play = 0; } if (!strcmp(type, "trak")) { l->trak++; } // extract audio config from within alac if (!strcmp(type, "alac") && bytes > len) { u8_t *ptr = streambuf->readp + 36; unsigned int block_size; l->play = l->trak; l->decoder = alac_create_decoder(len - 36, ptr, &l->sample_size, &l->sample_rate, &l->channels, &block_size); l->writebuf = malloc(block_size + 256); LOG_INFO("allocated write buffer of %u bytes", block_size); if (!l->writebuf) { LOG_ERROR("allocation failed"); return -1; } } // extract the total number of samples from stts if (!strcmp(type, "stsz") && bytes > len) { u32_t i; u8_t *ptr = streambuf->readp + 12; l->default_block_size = unpackN((u32_t *) ptr); ptr += 4; if (!l->default_block_size) { u32_t entries = unpackN((u32_t *)ptr); ptr += 4; l->block_size = malloc((entries + 1)* 4); for (i = 0; i < entries; i++) { l->block_size[i] = unpackN((u32_t *)ptr); ptr += 4; } l->block_size[entries] = 0; LOG_DEBUG("total blocksize contained in stsz %u", entries); } else { LOG_DEBUG("fixed blocksize in stsz %u", l->default_block_size); } } // extract the total number of samples from stts if (!strcmp(type, "stts") && bytes > len) { u32_t i; u8_t *ptr = streambuf->readp + 12; u32_t entries = unpackN((u32_t *)ptr); ptr += 4; for (i = 0; i < entries; ++i) { u32_t count = unpackN((u32_t *)ptr); u32_t size = unpackN((u32_t *)(ptr + 4)); l->sttssamples += count * size; ptr += 8; } LOG_DEBUG("total number of samples contained in stts: " FMT_u64, l->sttssamples); } // stash sample to chunk info, assume it comes before stco if (!strcmp(type, "stsc") && bytes > len && !l->chunkinfo) { l->stsc = malloc(len - 12); if (l->stsc == NULL) { LOG_WARN("malloc fail"); return -1; } memcpy(l->stsc, streambuf->readp + 12, len - 12); } // build offsets table from stco and stored stsc if (!strcmp(type, "stco") && bytes > len && l->play == l->trak) { u32_t i; // extract chunk offsets u8_t *ptr = streambuf->readp + 12; u32_t entries = unpackN((u32_t *)ptr); ptr += 4; l->chunkinfo = malloc(sizeof(struct chunk_table) * (entries + 1)); if (l->chunkinfo == NULL) { LOG_WARN("malloc fail"); return -1; } for (i = 0; i < entries; ++i) { l->chunkinfo[i].offset = unpackN((u32_t *)ptr); l->chunkinfo[i].sample = 0; ptr += 4; } l->chunkinfo[i].sample = 0; l->chunkinfo[i].offset = 0; // fill in first sample id for each chunk from stored stsc if (l->stsc) { u32_t stsc_entries = unpackN((u32_t *)l->stsc); u32_t sample = 0; u32_t last = 0, last_samples = 0; u8_t *ptr = (u8_t *)l->stsc + 4; while (stsc_entries--) { u32_t first = unpackN((u32_t *)ptr); u32_t samples = unpackN((u32_t *)(ptr + 4)); if (last) { for (i = last - 1; i < first - 1; ++i) { l->chunkinfo[i].sample = sample; sample += last_samples; } } if (stsc_entries == 0) { for (i = first - 1; i < entries; ++i) { l->chunkinfo[i].sample = sample; sample += samples; } } last = first; last_samples = samples; ptr += 12; } free(l->stsc); l->stsc = NULL; } } // found media data, advance to start of first chunk and return if (!strcmp(type, "mdat")) { _buf_inc_readp(streambuf, 8); l->pos += 8; bytes -= 8; if (l->play) { LOG_DEBUG("type: mdat len: %u pos: %u", len, l->pos); if (l->chunkinfo && l->chunkinfo[0].offset > l->pos) { u32_t skip = l->chunkinfo[0].offset - l->pos; LOG_DEBUG("skipping: %u", skip); if (skip <= bytes) { _buf_inc_readp(streambuf, skip); l->pos += skip; } else { l->consume = skip; } } l->sample = l->nextchunk = 1; l->block_index = 0; return 1; } else { LOG_DEBUG("type: mdat len: %u, no playable track found", len); return -1; } } // parse key-value atoms within ilst ---- entries to get encoder padding within iTunSMPB entry for gapless if (!strcmp(type, "----") && bytes > len) { u8_t *ptr = streambuf->readp + 8; u32_t remain = len - 8, size; if (!memcmp(ptr + 4, "mean", 4) && (size = unpackN((u32_t *)ptr)) < remain) { ptr += size; remain -= size; } if (!memcmp(ptr + 4, "name", 4) && (size = unpackN((u32_t *)ptr)) < remain && !memcmp(ptr + 12, "iTunSMPB", 8)) { ptr += size; remain -= size; } if (!memcmp(ptr + 4, "data", 4) && remain > 16 + 48) { // data is stored as hex strings: 0 start end samples u32_t b, c; u64_t d; if (sscanf((const char *)(ptr + 16), "%x %x %x " FMT_x64, &b, &b, &c, &d) == 4) { LOG_DEBUG("iTunSMPB start: %u end: %u samples: " FMT_u64, b, c, d); if (l->sttssamples && l->sttssamples < b + c + d) { LOG_DEBUG("reducing samples as stts count is less"); d = l->sttssamples - (b + c); } l->skip = b; l->samples = d; } } } // default to consuming entire box consume = len; // read into these boxes so reduce consume if (!strcmp(type, "moov") || !strcmp(type, "trak") || !strcmp(type, "mdia") || !strcmp(type, "minf") || !strcmp(type, "stbl") || !strcmp(type, "udta") || !strcmp(type, "ilst")) { consume = 8; } // special cases which mix mix data in the enclosing box which we want to read into if (!strcmp(type, "stsd")) consume = 16; if (!strcmp(type, "mp4a")) consume = 36; if (!strcmp(type, "meta")) consume = 12; // consume rest of box if it has been parsed (all in the buffer) or is not one we want to parse if (bytes >= consume) { LOG_DEBUG("type: %s len: %u consume: %u", type, len, consume); _buf_inc_readp(streambuf, consume); l->pos += consume; bytes -= consume; } else if ( !(!strcmp(type, "esds") || !strcmp(type, "stts") || !strcmp(type, "stsc") || !strcmp(type, "stsz") || !strcmp(type, "stco") || !strcmp(type, "----")) ) { LOG_DEBUG("type: %s len: %u consume: %u - partial consume: %u", type, len, consume, bytes); _buf_inc_readp(streambuf, bytes); l->pos += bytes; l->consume = consume - bytes; break; } else if (len > streambuf->size) { // can't process an atom larger than streambuf! LOG_ERROR("atom %s too large for buffer %u %u", type, len, streambuf->size); return -1; } else { // make sure there is 'len' contiguous space _buf_unwrap(streambuf, len); break; } } return 0; } static decode_state alac_decode(void) { size_t bytes; bool endstream; u8_t *iptr; u32_t frames, block_size; LOCK_S; // data not reached yet if (l->consume) { u32_t consume = min(l->consume, _buf_used(streambuf)); LOG_DEBUG("consume: %u of %u", consume, l->consume); _buf_inc_readp(streambuf, consume); l->pos += consume; l->consume -= consume; UNLOCK_S; return DECODE_RUNNING; } if (decode.new_stream) { int found = 0; // mp4 - read header found = read_mp4_header(); if (found == 1) { bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); LOG_INFO("setting track_start"); LOCK_O; output.next_sample_rate = decode_newstream(l->sample_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; } else if (found == -1) { LOG_WARN("[%p]: error reading stream header"); UNLOCK_S; return DECODE_ERROR; } else { // not finished header parsing come back next time UNLOCK_S; return DECODE_RUNNING; } } bytes = _buf_used(streambuf); block_size = l->default_block_size ? l->default_block_size : l->block_size[l->block_index]; // stream terminated if (stream.state <= DISCONNECT && (bytes == 0 || block_size == 0)) { UNLOCK_S; LOG_DEBUG("end of stream"); return DECODE_COMPLETE; } // is there enough data for decoding if (bytes < block_size) { UNLOCK_S; return DECODE_RUNNING; } else if (block_size != l->default_block_size) l->block_index++; bytes = min(bytes, _buf_cont_read(streambuf)); // need to create a buffer with contiguous data if (bytes < block_size) { iptr = malloc(block_size); memcpy(iptr, streambuf->readp, bytes); memcpy(iptr + bytes, streambuf->buf, block_size - bytes); } else iptr = streambuf->readp; if (!alac_to_pcm(l->decoder, iptr, l->writebuf, 2, &frames)) { LOG_ERROR("decode error"); UNLOCK_S; return DECODE_ERROR; } // and free it if (bytes < block_size) free(iptr); LOG_SDEBUG("block of %u bytes (%u frames)", block_size, frames); endstream = false; // mp4 end of chunk - skip to next offset if (l->chunkinfo && l->chunkinfo[l->nextchunk].offset && l->sample++ == l->chunkinfo[l->nextchunk].sample) { if (l->chunkinfo[l->nextchunk].offset > l->pos) { u32_t skip = l->chunkinfo[l->nextchunk].offset - l->pos; if (_buf_used(streambuf) >= skip) { _buf_inc_readp(streambuf, skip); l->pos += skip; } else { l->consume = skip; } l->nextchunk++; } else { LOG_ERROR("error: need to skip backwards!"); endstream = true; } // mp4 when not at end of chunk } else if (frames) { _buf_inc_readp(streambuf, block_size); l->pos += block_size; } else { endstream = true; } UNLOCK_S; if (endstream) { LOG_WARN("unable to decode further"); return DECODE_ERROR; } // now point at the beginning of decoded samples iptr = l->writebuf; if (l->skip) { u32_t skip; if (l->empty) { l->empty = false; l->skip -= frames; LOG_DEBUG("gapless: first frame empty, skipped %u frames at start", frames); } skip = min(frames, l->skip); LOG_DEBUG("gapless: skipping %u frames at start", skip); frames -= skip; l->skip -= skip; iptr += skip * l->channels * l->sample_size; } if (l->samples) { if (l->samples < frames) { LOG_DEBUG("gapless: trimming %u frames from end", frames - l->samples); frames = (u32_t) l->samples; } l->samples -= frames; } LOCK_O_direct; while (frames > 0) { size_t f, count; ISAMPLE_T *optr; IF_DIRECT( f = min(frames, _buf_cont_write(outputbuf) / BYTES_PER_FRAME); optr = (ISAMPLE_T *)outputbuf->writep; ); IF_PROCESS( f = min(frames, process.max_in_frames - process.in_frames); optr = (ISAMPLE_T *)((u8_t *) process.inbuf + process.in_frames * BYTES_PER_FRAME); ); f = min(f, frames); count = f; if (l->sample_size == 8) { while (count--) { *optr++ = ALIGN8(*iptr++); *optr++ = ALIGN8(*iptr++); } } else if (l->sample_size == 16) { u16_t *_iptr = (u16_t*) iptr; iptr += count * 4; while (count--) { *optr++ = ALIGN16(*_iptr++); *optr++ = ALIGN16(*_iptr++); } } else if (l->sample_size == 24) { while (count--) { *optr++ = ALIGN24(*(u32_t*) iptr); *optr++ = ALIGN24(*(u32_t*) (iptr + 3)); iptr += 6; } } else if (l->sample_size == 32) { u32_t *_iptr = (u32_t*) iptr; iptr += count * 8; while (count--) { *optr++ = ALIGN32(*_iptr++); *optr++ = ALIGN32(*_iptr++); } } else { LOG_ERROR("unsupported bits per sample: %u", l->sample_size); } frames -= f; IF_DIRECT( _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames = f; // called only if there is enough space in process buffer if (frames) LOG_ERROR("unhandled case"); ); } UNLOCK_O_direct; return DECODE_RUNNING; } static void alac_close(void) { if (l->decoder) alac_delete_decoder(l->decoder); if (l->writebuf) free(l->writebuf); if (l->chunkinfo) free(l->chunkinfo); if (l->block_size) free(l->block_size); if (l->stsc) free(l->stsc); memset(l, 0, sizeof(struct alac)); } static void alac_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { alac_close(); } struct codec *register_alac(void) { static struct codec ret = { 'l', // id "alc", // types MIN_READ, // min read MIN_SPACE, // min space assuming a ratio of 2 alac_open, // open alac_close, // close alac_decode, // decode }; l = calloc(1, sizeof(struct alac)); if (!l) return NULL; LOG_INFO("using alac to decode alc"); return &ret; } #endif /* ALAC */ squeezelite-1.9.9-1395+git20220104.874e4f9/alac_wrapper.cpp000066400000000000000000000045671416534420200222540ustar00rootroot00000000000000/* * alac_wrapper.c - ALAC decoder wrapper * * (c) Philippe, philippe_44@outlook.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include "ALACDecoder.h" #include "ALACBitUtilities.h" #include "alac_wrapper.h" typedef struct alac_codec_s { ALACDecoder *Decoder; unsigned block_size, frames_per_packet; } alac_codec_t; /*----------------------------------------------------------------------------*/ extern "C" struct alac_codec_s *alac_create_decoder(int magic_cookie_size, unsigned char *magic_cookie, unsigned char *sample_size, unsigned *sample_rate, unsigned char *channels, unsigned int *block_size) { struct alac_codec_s *codec = (struct alac_codec_s*) malloc(sizeof(struct alac_codec_s)); codec->Decoder = new ALACDecoder; codec->Decoder->Init(magic_cookie, magic_cookie_size); *channels = codec->Decoder->mConfig.numChannels; *sample_rate = codec->Decoder->mConfig.sampleRate; *sample_size = codec->Decoder->mConfig.bitDepth; codec->frames_per_packet = codec->Decoder->mConfig.frameLength; *block_size = codec->block_size = codec->frames_per_packet * (*channels) * (*sample_size) / 8; return codec; } /*----------------------------------------------------------------------------*/ extern "C" void alac_delete_decoder(struct alac_codec_s *codec) { delete (ALACDecoder*) codec->Decoder; free(codec); } /*----------------------------------------------------------------------------*/ extern "C" bool alac_to_pcm(struct alac_codec_s *codec, unsigned char* input, unsigned char *output, char channels, unsigned *out_frames) { BitBuffer input_buffer; BitBufferInit(&input_buffer, input, codec->block_size); return codec->Decoder->Decode(&input_buffer, output, codec->frames_per_packet, channels, out_frames) == ALAC_noErr; } squeezelite-1.9.9-1395+git20220104.874e4f9/alac_wrapper.h000066400000000000000000000027571416534420200217200ustar00rootroot00000000000000/***************************************************************************** * alac_wrapper.h: ALAC coder wrapper * * Copyright (C) 2016 Philippe * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************/ #ifndef __ALAC_WRAPPER_H_ #define __ALAC_WRAPPER_H_ struct alac_codec_s; #ifdef __cplusplus extern "C" { #endif struct alac_codec_s *alac_create_decoder(int magic_cookie_size, unsigned char *magic_cookie, unsigned char *sample_size, unsigned *sample_rate, unsigned char *channels, unsigned int *block_size); void alac_delete_decoder(struct alac_codec_s *codec); bool alac_to_pcm(struct alac_codec_s *codec, unsigned char* input, unsigned char *output, char channels, unsigned *out_frames); #ifdef __cplusplus } #endif #endifsqueezelite-1.9.9-1395+git20220104.874e4f9/alpine/000077500000000000000000000000001416534420200203445ustar00rootroot00000000000000squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/APKBUILD000066400000000000000000000041201416534420200214570ustar00rootroot00000000000000# Contributor: Carl Chave # Maintainer: Ralph Irving pkgname=squeezelite pkgver=1.9.7.1273 pkgrel=1 pkgdesc="Lightweight headless squeezebox player for Logitech Media Server" url="https://github.com/ralph-irving/squeezelite" arch="all" license="GPL-3.0-or-later3" options="!check" # No test suite depends="flac alsa-lib faad2 mpg123 libvorbis libmad soxr openssl opusfile libalac" makedepends="flac-dev alsa-lib-dev faad2-dev mpg123-dev libvorbis-dev libmad-dev soxr-dev openssl-dev opusfile-dev opus-dev libalac-dev" install="$pkgname.pre-install" subpackages="$pkgname-doc $pkgname-openrc" source="$pkgname-$pkgver.zip::https://github.com/ralph-irving/squeezelite/archive/master.zip load-libtremor-first.patch $pkgname.confd $pkgname.initd" builddir="$srcdir/$pkgname-master" build() { cd "$builddir" make OPTS="-DRESAMPLE -DDSD -DGPIO -DVISEXPORT -DUSE_SSL -DNO_SSLSYM -DOPUS -DALAC -I/usr/include/opus -I/usr/include/alac" gcc -Os -fomit-frame-pointer -fcommon -s -o find_servers tools/find_servers.c gcc -Os -fomit-frame-pointer -fcommon -s -o alsacap tools/alsacap.c -lasound } package() { cd "$builddir" install -Dm 755 squeezelite \ "$pkgdir"/usr/bin/squeezelite install -Dm 755 alsacap \ "$pkgdir"/usr/bin/alsacap install -Dm 755 find_servers \ "$pkgdir"/usr/bin/find_servers install -Dm 644 doc/squeezelite.1 \ "$pkgdir"/usr/share/man/man1/squeezelite.1 install -Dm 644 "$srcdir"/squeezelite.confd \ "$pkgdir"/etc/conf.d/squeezelite install -Dm 755 "$srcdir"/squeezelite.initd \ "$pkgdir"/etc/init.d/squeezelite } sha512sums="93d7695abb0bf670590cef73c2d9d484771e63acca39864b60cd819bf4b42fff87f9a82f7d419acce469c493d6bedfaba1207e646dde151bc76af959874f16cd load-libtremor-first.patch ff552fcbbbf2b2291958fa1c61057f54ba0d9b19620666036dd1e19897b5d7bcc543c40699c3ee53b2eec7a38b9cf46cb9205c2048f7d5488c23085d9904f114 squeezelite.confd fdc0358c10c56772d5e70178eac88f62729c22965c530ad989ebcc8aa4af8c871085bdeb8b4ccd96d8cf2e87388258180054d908719f81f2745d97654f6d60eb squeezelite.initd" squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/libalac/000077500000000000000000000000001416534420200217335ustar00rootroot00000000000000squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/libalac/APKBUILD000066400000000000000000000021611416534420200230510ustar00rootroot00000000000000# Maintainer: Ralph Irving pkgname=libalac pkgver=1.0.0 pkgrel=1 pkgdesc="Apple Lossless Audio Codec (ALAC) library" url="https://github.com/TimothyGu/alac" arch="all" subpackages="$pkgname-doc $pkgname-dev" license="Apache 2.0" makedepends="git autoconf automake libtool make tar" source="fix-arm-segfault.patch \ alac-version.patch" prepare() { cd "$builddir/../" git clone $url $pkgname-$pkgver cd $pkgname-$pkgver for patchfile in $source; do patch -p1 -i ../$patchfile done } build() { cd "$builddir" autoreconf -if ./configure \ --build=$CBUILD \ --host=$CHOST \ --prefix=/usr make -j1 } check() { cd "$builddir" make check } package() { cd "$builddir" make DESTDIR="$pkgdir" install install -Dm644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/COPYING } sha512sums="e72b13714476170108844b84c0043dc06d2ff2e8c9b651a7ad1571d148fc5567aca48048646d16fb82630d6972a31b9328f04e522972b297d1cf8e804785867f fix-arm-segfault.patch 093379f79b5dc9f5b8aa45826d61738b088d78305a7d514df33851ae34d02ee9034a8ecddf2558fcb1bf4daaf64c620ea4411521908cfc748e31fd0a2d50bbf7 alac-version.patch" squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/libalac/alac-version.patch000066400000000000000000000006601416534420200253410ustar00rootroot00000000000000--- libalac-1.0.0/configure.ac.orig 2020-09-06 10:55:00.450562777 -0400 +++ libalac-1.0.0/configure.ac 2020-09-06 10:56:39.308930134 -0400 @@ -14,7 +14,7 @@ dnl See the License for the specific language governing permissions and dnl limitations under the License. -AC_INIT([alac], [0.0r4+tg1], [nobody]) +AC_INIT([alac], [1.0.0], [nobody]) AC_CONFIG_AUX_DIR(.) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([tar-ustar foreign]) squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/libalac/fix-arm-segfault.patch000066400000000000000000000006241416534420200261310ustar00rootroot00000000000000diff --git a/codec/EndianPortable.c b/codec/EndianPortable.c index 5a7d5b8..b8423c9 100644 --- a/codec/EndianPortable.c +++ b/codec/EndianPortable.c @@ -40,6 +40,8 @@ #define TARGET_RT_LITTLE_ENDIAN 1 #elif defined (TARGET_OS_WIN32) #define TARGET_RT_LITTLE_ENDIAN 1 +#elif defined (__arm__) || defined(__aarch64__) +#define TARGET_RT_LITTLE_ENDIAN 1 #endif uint16_t Swap16NtoB(uint16_t inUInt16) squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/libtremor/000077500000000000000000000000001416534420200223435ustar00rootroot00000000000000squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/libtremor/APKBUILD000066400000000000000000000017621416534420200234670ustar00rootroot00000000000000# Maintainer: Ralph Irving pkgname=libtremor pkgver=0.19681 pkgrel=0 pkgdesc="Fixed Point Ogg Vorbis compliant software decoder library" url="https://svn.xiph.org/trunk/Tremor" arch="all" license="GPL-3.0-only" subpackages="$pkgname-doc $pkgname-dev" makedepends="libogg-dev subversion autoconf automake" depends="libogg" options="!check" build() { cd "$builddir/../" svn checkout https://svn.xiph.org/trunk/Tremor $pkgname-$pkgver cd $pkgname-$pkgver # Patch for error: # ./configure: line 9215: syntax error near unexpected token `,' # ./configure: line 9215: ` XIPH_PATH_OGG(, as_fn_error $? "must have Ogg installed!" "$LINENO" 5)' sed -i "s/\(XIPH_PATH_OGG(, AC_MSG_ERROR(must have Ogg installed!))\)/#\1/" configure.in ./autogen.sh \ --build=$CBUILD \ --host=$CHOST \ --prefix=/usr \ --enable-static=no make } package() { cd "$builddir" make DESTDIR="$pkgdir" install install -Dm644 COPYING "$pkgdir"/usr/share/licenses/$pkgname/COPYING } squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/load-libtremor-first.patch000066400000000000000000000011411416534420200254230ustar00rootroot00000000000000Index: vorbis.c =================================================================== --- squeezelite/vorbis.c (revision 1213) +++ squeezelite/vorbis.c (working copy) @@ -320,14 +320,14 @@ static bool load_vorbis() { #if !LINKALL - void *handle = dlopen(LIBVORBIS, RTLD_NOW); + void *handle = dlopen(LIBTREMOR, RTLD_NOW); char *err; - bool tremor = false; + bool tremor = true; if (!handle) { - handle = dlopen(LIBTREMOR, RTLD_NOW); + handle = dlopen(LIBVORBIS, RTLD_NOW); if (handle) { - tremor = true; + tremor = false; } else { LOG_INFO("dlerror: %s", dlerror()); return false; squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/squeezelite.confd000066400000000000000000000021461416534420200237210ustar00rootroot00000000000000# Copyright 1999-2017 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # Configuration for /etc/init.d/squeezelite # IP address of Logitech Media Server; leave this blank to try to # locate the server via auto-discovery. # If you fill this in then include "-s" before the IP address, eg: # SL_SERVER_IP="-s 1.2.3.4" SL_SERVERIP="" # User that Squeezelite should run as. The dedicated 'squeezelite' # user is preferred to avoid running with high privilege. This user # should be a member of the 'audio' group to allow access to the audio # hardware. Running as the 'root' user allows the sound output thread # to run at a very high priority -- this can help avoid gaps in # playback, but could be a potential security problem if there are # exploitable vulnerabilities in Squeezelite. SL_USER=squeezelite # Any other switches to pass to Squeezelite. See 'squeezelite -h' for # a description of all possible switches. # Example setting: # 1. the ALSA output device # 2. the player name # 3. turning on visualiser support (-v) # # SL_OPTS="-o sysdefault -n $HOSTNAME -v" # SL_OPTS="" squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/squeezelite.initd000066400000000000000000000011531416534420200237340ustar00rootroot00000000000000#!/sbin/openrc-run # Copyright 1999-2015 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 depend() { need net use alsasound after bootmisc } start() { ebegin "Starting squeezelite" start-stop-daemon \ --start \ --exec /usr/bin/squeezelite \ --pidfile /run/squeezelite.pid \ --make-pidfile \ --user ${SL_USER} \ --background \ -- ${SL_OPTS} ${SL_SERVERIP} eend $? } stop() { ebegin "Stopping squeezelite" start-stop-daemon \ --stop \ --exec /usr/bin/squeezelite \ --pidfile /run/squeezelite.pid eend $? } squeezelite-1.9.9-1395+git20220104.874e4f9/alpine/squeezelite.pre-install000066400000000000000000000001621416534420200250560ustar00rootroot00000000000000#!/bin/sh adduser -S -D -H -h /dev/null -s /sbin/nologin -G audio -g squeezelite squeezelite 2>/dev/null exit 0 squeezelite-1.9.9-1395+git20220104.874e4f9/buffer.c000066400000000000000000000106541416534420200205170ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // fifo bufffers #define _GNU_SOURCE #include "squeezelite.h" // _* called with muxtex locked #if !WIN inline #endif unsigned _buf_used(struct buffer *buf) { return buf->writep >= buf->readp ? buf->writep - buf->readp : buf->size - (buf->readp - buf->writep); } unsigned _buf_space(struct buffer *buf) { return buf->size - _buf_used(buf) - 1; // reduce by one as full same as empty otherwise } unsigned _buf_cont_read(struct buffer *buf) { return buf->writep >= buf->readp ? buf->writep - buf->readp : buf->wrap - buf->readp; } unsigned _buf_cont_write(struct buffer *buf) { return buf->writep >= buf->readp ? buf->wrap - buf->writep : buf->readp - buf->writep; } void _buf_inc_readp(struct buffer *buf, unsigned by) { buf->readp += by; if (buf->readp >= buf->wrap) { buf->readp -= buf->size; } } void _buf_inc_writep(struct buffer *buf, unsigned by) { buf->writep += by; if (buf->writep >= buf->wrap) { buf->writep -= buf->size; } } void buf_flush(struct buffer *buf) { mutex_lock(buf->mutex); buf->readp = buf->buf; buf->writep = buf->buf; mutex_unlock(buf->mutex); } // adjust buffer to multiple of mod bytes so reading in multiple always wraps on frame boundary void buf_adjust(struct buffer *buf, size_t mod) { size_t size; mutex_lock(buf->mutex); size = ((unsigned)(buf->base_size / mod)) * mod; buf->readp = buf->buf; buf->writep = buf->buf; buf->wrap = buf->buf + size; buf->size = size; mutex_unlock(buf->mutex); } // called with mutex locked to resize, does not retain contents, reverts to original size if fails void _buf_resize(struct buffer *buf, size_t size) { free(buf->buf); buf->buf = malloc(size); if (!buf->buf) { size = buf->size; buf->buf= malloc(size); if (!buf->buf) { size = 0; } } buf->readp = buf->buf; buf->writep = buf->buf; buf->wrap = buf->buf + size; buf->size = size; buf->base_size = size; } void _buf_unwrap(struct buffer *buf, size_t cont) { ssize_t len, by = cont - (buf->wrap - buf->readp); size_t size; u8_t *scratch; // do nothing if we have enough space if (by <= 0 || cont >= buf->size) return; // buffer already unwrapped, just move it up if (buf->writep >= buf->readp) { memmove(buf->readp - by, buf->readp, buf->writep - buf->readp); buf->readp -= by; buf->writep -= by; return; } // how much is overlapping size = by - (buf->readp - buf->writep); len = buf->writep - buf->buf; // buffer is wrapped and enough free space to move data up directly if (size <= 0) { memmove(buf->readp - by, buf->readp, buf->wrap - buf->readp); buf->readp -= by; memcpy(buf->wrap - by, buf->buf, min(len, by)); if (len > by) { memmove(buf->buf, buf->buf + by, len - by); buf->writep -= by; } else buf->writep += buf->size - by; return; } scratch = malloc(size); // buffer is wrapped but not enough free room => use scratch zone if (scratch) { memcpy(scratch, buf->writep - size, size); memmove(buf->readp - by, buf->readp, buf->wrap - buf->readp); buf->readp -= by; memcpy(buf->wrap - by, buf->buf, by); memmove(buf->buf, buf->buf + by, len - by - size); buf->writep -= by; memcpy(buf->writep - size, scratch, size); free(scratch); } else { _buf_unwrap(buf, cont / 2); _buf_unwrap(buf, cont - cont / 2); } } void buf_init(struct buffer *buf, size_t size) { buf->buf = malloc(size); buf->readp = buf->buf; buf->writep = buf->buf; buf->wrap = buf->buf + size; buf->size = size; buf->base_size = size; mutex_create_p(buf->mutex); } void buf_destroy(struct buffer *buf) { if (buf->buf) { free(buf->buf); buf->buf = NULL; buf->size = 0; buf->base_size = 0; mutex_destroy(buf->mutex); } } squeezelite-1.9.9-1395+git20220104.874e4f9/daemonize.c000066400000000000000000000056571416534420200212300ustar00rootroot00000000000000/* * Copyright (c) 2013 Ralph Irving * * daemonize.c is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * daemonize.c is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with SlimProtoLib; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "squeezelite.h" #include #include void fork_child_handler(int signum) { switch(signum) { case SIGALRM: exit(EXIT_FAILURE); break; case SIGUSR1: exit(EXIT_SUCCESS); break; case SIGCHLD: exit(EXIT_FAILURE); break; } } pid_t parent; void init_daemonize() { pid_t pid, sid; /* already a daemon */ if ( getppid() == 1 ) return; /* Trap signals that we expect to recieve */ signal(SIGCHLD,fork_child_handler); signal(SIGUSR1,fork_child_handler); signal(SIGALRM,fork_child_handler); /* Fork off the parent process */ pid = fork(); if (pid < 0) { syslog( LOG_ERR, "unable to fork daemon, code=%d (%s)", errno, strerror(errno) ); exit(EXIT_FAILURE); } /* If we got a good PID, then we can exit the parent process. */ if (pid > 0) { /* Wait for confirmation from the child via SIGTERM or SIGCHLD, or for two seconds to elapse (SIGALRM). pause() should not return. */ alarm(2); pause(); exit(EXIT_FAILURE); } /* At this point we are executing as the child process */ parent = getppid(); /* Cancel certain signals */ signal(SIGCHLD,SIG_DFL); /* A child process dies */ signal(SIGTSTP,SIG_IGN); /* Various TTY signals */ signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */ signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */ /* Change the file mode mask */ umask(0); /* Create a new SID for the child process */ sid = setsid(); if (sid < 0) { syslog( LOG_ERR, "unable to create a new session, code %d (%s)", errno, strerror(errno) ); exit(EXIT_FAILURE); } /* Change the current working directory. This prevents the current directory from being locked; hence not being able to remove it. */ if ((chdir("/")) < 0) { syslog( LOG_ERR, "unable to change directory to %s, code %d (%s)", "/", errno, strerror(errno) ); exit(EXIT_FAILURE); } } int daemon( int nochdir, int noclose ) { if ( ! noclose ) { /* Redirect standard files to /dev/null */ freopen( "/dev/null", "r", stdin); freopen( "/dev/null", "w", stdout); freopen( "/dev/null", "w", stderr); } /* Tell the parent process that we are A-okay */ kill( parent, SIGUSR1 ); return 0; } squeezelite-1.9.9-1395+git20220104.874e4f9/decode.c000066400000000000000000000200171416534420200204630ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // decode thread #include "squeezelite.h" log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct processstate process; struct decodestate decode; struct codec *codecs[MAX_CODECS]; struct codec *codec; static bool running = true; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #define LOCK_D mutex_lock(decode.mutex); #define UNLOCK_D mutex_unlock(decode.mutex); #if PROCESS #define IF_DIRECT(x) if (decode.direct) { x } #define IF_PROCESS(x) if (!decode.direct) { x } #define MAY_PROCESS(x) { x } #else #define IF_DIRECT(x) { x } #define IF_PROCESS(x) #define MAY_PROCESS(x) #endif static void *decode_thread() { while (running) { size_t bytes, space, min_space; bool toend; bool ran = false; LOCK_S; bytes = _buf_used(streambuf); toend = (stream.state <= DISCONNECT); UNLOCK_S; LOCK_O; space = _buf_space(outputbuf); UNLOCK_O; LOCK_D; if (decode.state == DECODE_RUNNING && codec) { LOG_SDEBUG("streambuf bytes: %u outputbuf space: %u", bytes, space); IF_DIRECT( min_space = codec->min_space; ); IF_PROCESS( min_space = process.max_out_frames * BYTES_PER_FRAME; ); if (space > min_space && (bytes > codec->min_read_bytes || toend)) { decode.state = codec->decode(); IF_PROCESS( if (process.in_frames) { process_samples(); } if (decode.state == DECODE_COMPLETE) { process_drain(); } ); if (decode.state != DECODE_RUNNING) { LOG_INFO("decode %s", decode.state == DECODE_COMPLETE ? "complete" : "error"); LOCK_O; if (output.fade_mode) _checkfade(false); UNLOCK_O; wake_controller(); } ran = true; } } UNLOCK_D; if (!ran) { usleep(100000); } } return 0; } static void sort_codecs(int pry, struct codec* ptr) { static int priority[MAX_CODECS]; int i, tpry; struct codec* tptr; for (i = 0; i < MAX_CODECS; i++) { if (!codecs[i]) { codecs[i] = ptr; priority[i] = pry; return; } if (pry < priority[i]) { tptr = codecs[i]; codecs[i] = ptr; ptr = tptr; tpry = priority[i]; priority[i] = pry; pry = tpry; } } } static thread_type thread; void decode_init(log_level level, const char *include_codecs, const char *exclude_codecs) { int i; char* order_codecs; loglevel = level; LOG_INFO("init decode"); // register codecs // dsf,dff,alc,wma,wmap,wmal,aac,spt,ogg,ogf,flc,aif,pcm,mp3 i = 0; #if DSD if (!strstr(exclude_codecs, "dsd") && (!include_codecs || (order_codecs = strstr(include_codecs, "dsd")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_dsd()); #endif #if ALAC if (!strstr(exclude_codecs, "alac") && (!include_codecs || (order_codecs = strstr(include_codecs, "alac")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_alac()); #elif FFMPEG if (!strstr(exclude_codecs, "alac") && (!include_codecs || (order_codecs = strstr(include_codecs, "alac")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_ff("alc")); if (!strstr(exclude_codecs, "wma") && (!include_codecs || (order_codecs = strstr(include_codecs, "wma")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_ff("wma")); #endif #ifndef NO_FAAD if (!strstr(exclude_codecs, "aac") && (!include_codecs || (order_codecs = strstr(include_codecs, "aac")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_faad()); #endif if (!strstr(exclude_codecs, "ogg") && (!include_codecs || (order_codecs = strstr(include_codecs, "ogg")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_vorbis()); #if OPUS if (!strstr(exclude_codecs, "ops") && (!include_codecs || (order_codecs = strstr(include_codecs, "ops")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_opus()); #endif if (!strstr(exclude_codecs, "flac") && (!include_codecs || (order_codecs = strstr(include_codecs, "flac")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_flac()); if (!strstr(exclude_codecs, "pcm") && (!include_codecs || (order_codecs = strstr(include_codecs, "pcm")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_pcm()); // try mad then mpg for mp3 unless command line option passed if (!(strstr(exclude_codecs, "mp3") || strstr(exclude_codecs, "mad")) && (!include_codecs || (order_codecs = strstr(include_codecs, "mp3")) || (order_codecs = strstr(include_codecs, "mad")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_mad()); else if (!(strstr(exclude_codecs, "mp3") || strstr(exclude_codecs, "mpg")) && (!include_codecs || (order_codecs = strstr(include_codecs, "mp3")) || (order_codecs = strstr(include_codecs, "mpg")))) sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_mpg()); LOG_DEBUG("include codecs: %s exclude codecs: %s", include_codecs ? include_codecs : "", exclude_codecs); mutex_create(decode.mutex); #if LINUX || OSX || FREEBSD pthread_attr_t attr; pthread_attr_init(&attr); #ifdef PTHREAD_STACK_MIN pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + DECODE_THREAD_STACK_SIZE); #endif pthread_create(&thread, &attr, decode_thread, NULL); pthread_attr_destroy(&attr); #endif #if WIN thread = CreateThread(NULL, DECODE_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&decode_thread, NULL, 0, NULL); #endif decode.new_stream = true; decode.state = DECODE_STOPPED; MAY_PROCESS( decode.direct = true; decode.process = false; ); } void decode_close(void) { LOG_INFO("close decode"); LOCK_D; if (codec) { codec->close(); codec = NULL; } running = false; UNLOCK_D; #if LINUX || OSX || FREEBSD pthread_join(thread, NULL); #endif mutex_destroy(decode.mutex); } void decode_flush(void) { LOG_INFO("decode flush"); LOCK_D; decode.state = DECODE_STOPPED; IF_PROCESS( process_flush(); ); UNLOCK_D; } unsigned decode_newstream(unsigned sample_rate, unsigned supported_rates[]) { // called with O locked to get sample rate for potentially processed output stream // release O mutex during process_newstream as it can take some time MAY_PROCESS( if (decode.process) { UNLOCK_O; sample_rate = process_newstream(&decode.direct, sample_rate, supported_rates); LOCK_O; } ); return sample_rate; } void codec_open(u8_t format, u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness) { int i; LOG_INFO("codec open: '%c'", format); LOCK_D; decode.new_stream = true; decode.state = DECODE_STOPPED; MAY_PROCESS( decode.direct = true; // potentially changed within codec when processing enabled ); // find the required codec for (i = 0; i < MAX_CODECS; ++i) { if (codecs[i] && codecs[i]->id == format) { if (codec && codec != codecs[i]) { LOG_INFO("closing codec: '%c'", codec->id); codec->close(); } codec = codecs[i]; codec->open(sample_size, sample_rate, channels, endianness); decode.state = DECODE_READY; UNLOCK_D; return; } } UNLOCK_D; LOG_ERROR("codec not found"); } squeezelite-1.9.9-1395+git20220104.874e4f9/doc/000077500000000000000000000000001416534420200176415ustar00rootroot00000000000000squeezelite-1.9.9-1395+git20220104.874e4f9/doc/squeezelite.1000066400000000000000000000277741416534420200223030ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" (C) Copyright 2013-4 Chris Boot .\" .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH SQUEEZELITE 1 "2020-07-16" "Debian Project" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME squeezelite \- Lightweight headless Squeezebox emulator .SH SYNOPSIS .B squeezelite .RI [ options ] .SH DESCRIPTION .B Squeezelite is a small headless Logitech Squeezebox emulator. It is aimed at supporting high quality audio including USB DAC based output at multiple sample rates. .PP The player is controlled using, and media is streamed from, a Logitech Media Server instance running somewhere on the local network. .SH OPTIONS This program supports the following options: .TP .B \-? Show a summary of the available command-line options. .TP .B \-s [:] Connect to the specified Logitech Media Server, otherwise uses automatic discovery to find server on the local network. This option should only be needed if automatic discovery does not work, or the server is not on the local network segment (e.g. behind a router). .TP .B \-o Specify the audio output device; the default value is .IR default . Use the .B \-l option to list available output devices. .I - can be used to output raw samples to standard output. .TP .B \-l List available audio output devices to stdout and exit. These device names can be passed to the .B \-o option in order to select a particular device or configuration to use for audio playback. .TP .B \-a Specify parameters used when opening an audio output device. .PP .RS For ALSA, the format .B :

::: is used where .B is the buffer time in milliseconds (values less than 500) or size in bytes (default .IR 40 ms); .B

is the period count (values less than 50) or size in bytes (default .IR 4 " periods);" .B is the sample format (possible values: .IR 16 ", " 24 ", " 24_3 " or " 32 ); .B is whether to use mmap (possible values: .IR 0 " or " 1 ). .B open ALSA output device twice. (possible values: .IR 0 " or " 1 ). .RE .RS .PP For Linux PortAudio, the value .B is simply the target latency in milliseconds. .RE .RS .PP For MacOS, .B : .B is target latency in milliseconds. .B open device in Pro Mode or Play Nice (respective values: .IR 0 " or " 1 ). .RE .RS .PP For Windows, .B : .B is target latency in milliseconds. .B use exclusive mode for WASAPI (possible values: .IR 0 " or " 1 ). .RE .RS .PP When the output is sent to standard output, the value can be .IR 16 ", " 24 " or " 32 , which denotes the sample size in bits. Little Endian only. .RE .TP .B \-b : Specify internal stream and output buffer sizes in kilobytes. Default is 2048:3446. .TP .B \-c ,... Restrict codecs to those specified, otherwise load all available codecs. Use .B squeezelite -? to obtain the list of codecs built into \fBsqueezelite\fR. .TP .B \-C Close the output device after .B seconds of the player being idle; the default is to always keep the device open as long as the payer is "on". .TP .B \-d = Set logging level. Categories are: .IR all ", " slimproto ", " stream ", " decode ", " output " or " ir . Levels can be: .IR info ", " debug " or " sdebug . The option can be repeated to set different log levels for different categories. .TP .B \-e ,... Explicitly exclude native support of one or more codecs. See also .BR \-c , above. .TP .B \-f Send logging output to a log file instead of standard output or standard error. .TP .B \-G : Specify the BCM GPIO# to use for Amp Power Relay and if the output should be Active High or Low. This cannot be used with the \fB-S\fR option. .TP .B \-i [] Enable LIRC remote control support. If the optional .B is not provided, .I ~/.lircrc is used instead. .TP .B \-m Override the player's MAC address. The format must be colon-delimited hexadecimal, for example: ab:cd:ef:12:34:56. This is usually automatically detected, and should not need to be provided in most circumstances. .TP .B \-M Override the player's hardware model name. The default value is .IR SqueezeLite . .TP .B \-n Set the player name. This name is used by the Logitech Media Server to refer to the player by name. This option is mututally exclusive with .BR \-N . .TP .B \-N Allow the server to set the player's name. The player name is stored in the file pointed to by .B so that it can persist between restarts. This option is mututally exclusive with .BR \-n . .TP .B \-O Specify mixer device, defaults to \fB\fR. \. .TP .B \-p Set real time priority of output thread (1-99; default .IR 45 ). Not applicable when using PortAudio. .TP .B \-P Write the process ID (PID) number to the given .BR . This may be useful when running \fBsqueezelite\fR as a daemon. .TP .B \-r [:] Specify sample rates supported by the output device; this is required if the output device is switched off when \fBsqueezelite\fR is started. The format is either a single maximum sample rate, a range of sample rates in the format .IR - , or a comma-separated list of available rates. Delay is an optional time to wait when switching sample rates between tracks, in milliseconds. .TP .B \-S Absolute path to script to launch on power commands from LMS. This cannot be used with the \fB-G\fR option. .TP .B \-u|-R [params] Enable upsampling of played audio. The argument is optional; see .B RESAMPLING (below) for more information. The options .BR -u " and " -R are synonymous. .TP .B \-D [delay] Output device supports DSD over PCM (DoP). DSD streams will be converted to DoP before output. If this option is not supplied, DSD streams will be converted to PCM and resampled, so they can be played on a PCM DAC. Delay is an optional time to wait when switching between PCM and DoP between tracks, in milliseconds. .TP .B \-v Enable visualiser support. This creates a shared memory segment that contains some of the audio being played, so that an external visualiser can read and process this to create visualisations. .TP .B \-W Read wave and aiff format from header, ignoring server parameters. .TP .B \-L List available volume controls for the output device. Only applicable when using ALSA output. .TP .B \-U Unmute the given ALSA .B at daemon startup and set it to full volume. Use software volume adjustment for playback. This option is mutually exclusive with the \fB\-V\fR option. Only applicable when using ALSA output. .TP .B \-V Use the given ALSA .B for volume adjustment during playback. This prevents the use of software volume control within \fBsqueezelite\fR. This option is mutually exclusive with the \fB\-U\fR option. If neither \fB\-U\fR nor \fB\-V\fR options are provided, no ALSA controls are adjusted while running \fBsqueezelite\fR and software volume control is used instead. Only applicable when using ALSA output. .TP .B \-X Use linear volume adjustments instead of in terms of dB (only for hardware volume control). .TP .B \-z Cause \fBsqueezelite\fR to run as a daemon. That is, it detaches itself from the terminal and runs in the background. .TP .B \-Z Report rate to server in helo as the maximum sample rate we can support. .TP .B \-t Display version and license information. .SH RESAMPLING Audio can be resampled or upsampled before being sent to the output device. This can be enabled simply by passing the \fB\-u\fR option to \fBsqueezelite\fR, but further configuration can be given as an argument to the option. .PP Resampling is performed using the SoX Resampler library; the documentation for that library and the SoX \fIrate\fR effect many be helpful when configuring upsampling for \fBsqueezelite\fR. .PP The format of the argument is .B :::::: .SS recipe This part of the argument string is made up of a number of single-character flags: \fB[v|h|m|l|q][L|I|M][s][E|X]\fR. The default value is \fBhL\fR. .TP .IR v ", " h ", " m ", " l " or " q are mutually exclusive and correspond to very high, high, medium, low or quick quality. .TP .IR L ", " I " or " M correspond to linear, intermediate or minimum phase. .TP .IR s changes resampling bandwidth from the default 95% (based on the 3dB point) to 99%. .TP .IR E exception - avoids resampling if the output device supports the playback sample rate natively. .TP .IR X resamples to the maximum sample rate for the output device ("asynchronous" resampling). .TP .B Examples .B \-u vLs would use very high quality setting, linear phase filter and steep cut-off. .br .B \-u hM would specify high quality, with the minimum phase filter. .br .B \-u hMX would specify high quality, with the minimum phase filter and async upsampling to max device rate. .SS flags The second optional argument to \fB\-u\fR allows the user to specify the following arguments (taken from the \fIsoxr.h\fR header file), in hex: .sp #define SOXR_ROLLOFF_SMALL 0u /* <= 0.01 dB */ .br #define SOXR_ROLLOFF_MEDIUM 1u /* <= 0.35 dB */ .br #define SOXR_ROLLOFF_NONE 2u /* For Chebyshev bandwidth. */ .sp #define SOXR_MAINTAIN_3DB_PT 4u /* Reserved for internal use. */ .br #define SOXR_HI_PREC_CLOCK 8u /* Increase 'irrational' ratio accuracy. */ .br #define SOXR_DOUBLE_PRECISION 16u /* Use D.P. calcs even if precision <= 20. */ .br #define SOXR_VR 32u /* Experimental, variable-rate resampling. */ .TP .B Examples .B \-u :2 would specify \fBSOXR_ROLLOFF_NONE\fR. .sp \fBNB:\fR In the example above the first option, \fB\fR, has not been specified so would default to \fBhL\fR. Therefore, specifying \fB\-u :2\fR is equivalent to having specified \fB\-u hL:2\fR. .SS attenuation Internally, data is passed to the SoX resample process as 32 bit integers and output from the SoX resample process as 32 bit integers. Why does this matter? There is the possibility that integer samples, once resampled may be clipped (i.e. exceed the maximum value). By default, if you do not specify an \fBattenuation\fR value, it will default to \-1db. A value of \fI0\fR on the command line, i.e. \fB-u ::0\fR will disable the default \-1db attenuation being applied. .sp \fBNB:\fR Clipped samples will be logged. Keep an eye on the log file. .TP .B Examples .B \-u ::6 specifies to apply \-6db (ie. halve the volume) prior to the resampling process. .SS precision The internal 'bit' precision used in the re-sampling calculations (ie. quality). .sp \fBNB:\fR HQ = 20, VHQ = 28. .TP .B Examples .B \-u :::28 specifies 28-bit precision. .SS passband_end A percentage value between 0 and 100, where 100 is the Nyquist frequency. The default if not explicitly set is \fI91.3\fR. .TP .B Examples .B \-u ::::98 specifies passband ends at 98 percent of the Nyquist frequency. .SS stopband_start A percentage value between 0 and 100, where 100 is the Nyquist frequency. The default if not explicitly set is \fI100\fR. .TP .B Examples .B \-u :::::100 specifies that the stopband starts at the Nyquist frequency. .SS phase_response A value between 0-100, where \fI0\fR is equivalent to the recipe \fIM\fR flag for minimum phase, \fI25\fR is equivalent to the recipe \fII\fR flag for intermediate phase and \fI50\fR is equivalent to the recipe \fIL\fR flag for linear phase. .TP .B Examples .B \-u ::::::50 specifies linear phase. .SH SEE ALSO .TP http://wiki.slimdevices.com/index.php/Squeezelite .TP http://wiki.slimdevices.com/index.php/Logitech_Media_Server .TP sox(1) for further information about resampling. squeezelite-1.9.9-1395+git20220104.874e4f9/dop.c000066400000000000000000000044771416534420200200360ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012, 2013, triode1@btinternet.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // DSP over PCM (DOP) specific functions #include "squeezelite.h" #if DSD extern struct buffer *outputbuf; extern struct outputstate output; #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) // check for 32 dop marker frames to see if this is a dop stream // dop is always encoded in 24 bit samples with markers 0x05 or 0xFA in MSB bool is_stream_dop(u8_t *lptr, u8_t *rptr, int step, frames_t frames) { int matched = 0; u32_t next = 0; while (frames--) { if ((*lptr == 0x05 && *rptr == 0x05) || (*lptr == 0xFA && *rptr == 0xFA)) { if (*lptr == next) { matched++; } else { next = *lptr; matched = 1; } next = ( 0x05 + 0xFA ) - next; } else { return false; } if (matched == 32) { return true; } lptr+=step; rptr+=step; } return false; } // update the dop marker and potentially invert polarity for frames in the output buffer // performaned on all output including silence to maintain marker phase consitency void update_dop(u32_t *ptr, frames_t frames, bool invert) { static u32_t marker = 0x05; if (!invert) { while (frames--) { u32_t scaled_marker = marker << 24; *ptr = (*ptr & 0x00FFFF00) | scaled_marker; ++ptr; *ptr = (*ptr & 0x00FFFF00) | scaled_marker; ++ptr; marker = ( 0x05 + 0xFA ) - marker; } } else { while (frames--) { u32_t scaled_marker = marker << 24; *ptr = ((~(*ptr)) & 0x00FFFF00) | scaled_marker; ++ptr; *ptr = ((~(*ptr)) & 0x00FFFF00) | scaled_marker; ++ptr; marker = ( 0x05 + 0xFA ) - marker; } } } #endif // DSD squeezelite-1.9.9-1395+git20220104.874e4f9/dsd.c000066400000000000000000000573171416534420200200270ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // dsd support #include "squeezelite.h" #if DSD // use dsd2pcm from Sebastian Gesemann for conversion to pcm: #include "./dsd2pcm/dsd2pcm.h" extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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 #define BLOCK 4096 // expected size of dsd block #define BLOCK_FRAMES BLOCK * BYTES_PER_FRAME #define WRAP_BUF_SIZE 32 // max 4 bytes per frame and 8 channels typedef enum { UNKNOWN=0, DSF, DSDIFF } dsd_type; static dsd_format outfmt = PCM; // local copy of output.dsdfmt to avoid holding output lock struct dsd { dsd_type type; u32_t consume; u32_t sample_rate; u32_t channels; u64_t sample_bytes; u32_t block_size; bool lsb_first; dsd2pcm_ctx *dsd2pcm_ctx[2]; float *transfer[2]; }; static struct dsd *d; static u64_t unpack64be(const u8_t *p) { return (u64_t)p[0] << 56 | (u64_t)p[1] << 48 | (u64_t)p[2] << 40 | (u64_t)p[3] << 32 | (u64_t)p[4] << 24 | (u64_t)p[5] << 16 | (u64_t)p[6] << 8 | (u64_t)p[7]; } static u64_t unpack64le(const u8_t *p) { return (u64_t)p[7] << 56 | (u64_t)p[6] << 48 | (u64_t)p[5] << 40 | (u64_t)p[4] << 32 | (u64_t)p[3] << 24 | (u64_t)p[2] << 16 | (u64_t)p[1] << 8 | (u64_t)p[0]; } static u32_t unpack32le(const u8_t *p) { return (u32_t)p[3] << 24 | (u32_t)p[2] << 16 | (u32_t)p[1] << 8 | (u32_t)p[0]; } static int _read_header(void) { unsigned bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); s32_t consume; if (!d->type && bytes >= 4) { if (!memcmp(streambuf->readp, "FRM8", 4)) { d->type = DSDIFF; } else if (!memcmp(streambuf->readp, "DSD ", 4)) { d->type = DSF; } else { LOG_WARN("bad type"); return -1; } } while (bytes >= 16) { char id[5]; u64_t len = d->type == DSDIFF ? unpack64be(streambuf->readp + 4) : unpack64le(streambuf->readp + 4); memcpy(id, streambuf->readp, 4); id[4] = '\0'; consume = 0; if (d->type == DSDIFF) { if (!strcmp(id, "FRM8")) { if (!memcmp(streambuf->readp + 12, "DSD ", 4)) { consume = 16; // read into } else { LOG_WARN("bad dsdiff FRM8"); return -1; } } if (!strcmp(id, "PROP") && !memcmp(streambuf->readp + 12, "SND ", 4)) { consume = 16; // read into } if (!strcmp(id, "FVER")) { LOG_INFO("DSDIFF version: %u.%u.%u.%u", *(streambuf->readp + 12), *(streambuf->readp + 13), *(streambuf->readp + 14), *(streambuf->readp + 15)); } if (!strcmp(id, "FS ")) { d->sample_rate = unpackN((void *)(streambuf->readp + 12)); LOG_INFO("sample rate: %u", d->sample_rate); } if (!strcmp(id, "CHNL")) { d->channels = unpackn((void *)(streambuf->readp + 12)); LOG_INFO("channels: %u", d->channels); } if (!strcmp(id, "DSD ")) { LOG_INFO("found dsd len: " FMT_u64, len); d->sample_bytes = len; _buf_inc_readp(streambuf, 12); bytes -= 12; return 1; // got to the audio } } if (d->type == DSF) { if (!strcmp(id, "fmt ")) { if (bytes >= len && bytes >= 52) { u32_t version = unpack32le((void *)(streambuf->readp + 12)); u32_t format = unpack32le((void *)(streambuf->readp + 16)); LOG_INFO("DSF version: %u format: %u", version, format); if (format != 0) { LOG_WARN("only support DSD raw format"); return -1; } d->channels = unpack32le((void *)(streambuf->readp + 24)); d->sample_rate = unpack32le((void *)(streambuf->readp + 28)); d->lsb_first = (unpack32le((void *)(streambuf->readp + 32)) == 1); d->sample_bytes = unpack64le((void *)(streambuf->readp + 36)) / 8; d->block_size = unpack32le((void *)(streambuf->readp + 44)); LOG_INFO("channels: %u", d->channels); LOG_INFO("sample rate: %u", d->sample_rate); LOG_INFO("lsb first: %u", d->lsb_first); LOG_INFO("sample bytes: " FMT_u64, d->sample_bytes); LOG_INFO("block size: %u", d->block_size); } else { consume = -1; // come back later } } if (!strcmp(id, "data")) { LOG_INFO("found dsd len: " FMT_u64, len); _buf_inc_readp(streambuf, 12); bytes -= 12; return 1; // got to the audio } } // default to consuming whole chunk if (!consume) { consume = (s32_t)((d->type == DSDIFF) ? len + 12 : len); } if (bytes >= consume) { LOG_DEBUG("id: %s len: " FMT_u64 " consume: %d", id, len, consume); _buf_inc_readp(streambuf, consume); bytes -= consume; } else if (consume > 0) { LOG_DEBUG("id: %s len: " FMT_u64 " consume: %d - partial consume: %u", id, len, consume, bytes); _buf_inc_readp(streambuf, bytes); d->consume = consume - bytes; break; } else { break; } } return 0; } static decode_state _decode_dsf(void) { // samples in streambuf are interleaved on block basis // we transfer whole blocks for all channels in one call and so itterate the while loop below to handle wraps unsigned bytes = _buf_used(streambuf); unsigned block_left = d->block_size; unsigned padding = 0; unsigned bytes_per_frame; switch (outfmt) { case DSD_U32_LE: case DSD_U32_BE: bytes_per_frame = 4; break; case DSD_U16_LE: case DSD_U16_BE: case DOP: case DOP_S24_LE: case DOP_S24_3LE: bytes_per_frame = 2; break; default: bytes_per_frame = 1; } if (bytes < d->block_size * d->channels) { LOG_INFO("stream too short"); // this can occur when scanning the track return DECODE_COMPLETE; } IF_PROCESS( process.in_frames = 0; ); while (block_left) { frames_t frames, out, count; unsigned bytes_read; u8_t *iptrl = (u8_t *)streambuf->readp; u8_t *iptrr = (u8_t *)streambuf->readp + d->block_size; u32_t *optr; if (iptrr >= streambuf->wrap) { iptrr -= streambuf->size; } // Remove zero padding from last block in case of inaccurate sample count if ((_buf_used(streambuf) == d->block_size * d->channels) && (d->sample_bytes > _buf_used(streambuf))) { int i; u8_t *ipl, *ipr; for (i = d->block_size - 1; i > 0; i--) { ipl = iptrl + i; if (ipl >= streambuf->wrap) ipl -= streambuf->size; ipr = iptrr + i; if (ipr >= streambuf->wrap) ipr -= streambuf->size; if (*ipl || *ipr) break; padding++; } block_left -= padding; } bytes = min(block_left, min(streambuf->wrap - iptrl, streambuf->wrap - iptrr)); IF_DIRECT( out = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; optr = (u32_t *)outputbuf->writep; ); IF_PROCESS( out = process.max_in_frames - process.in_frames; optr = (u32_t *)(process.inbuf + process.in_frames * BYTES_PER_FRAME); ); frames = min(bytes, d->sample_bytes) / bytes_per_frame; if (frames == 0) { if (d->sample_bytes && bytes >= (2 * d->sample_bytes)) { // byte(s) left fill frame with silence byte(s) and play int i; for (i = d->sample_bytes; i < bytes_per_frame; i++) *(iptrl + i) = *(iptrr + i) = 0x69; frames = 1; } else { // should not get here due to wrapping m/2 for dsd should never result in 0 as header len is always even LOG_INFO("frames got to zero"); return DECODE_COMPLETE; } } frames = min(frames, out); frames = min(frames, BLOCK); bytes_read = frames * bytes_per_frame; count = frames; switch (outfmt) { case DSD_U32_LE: case DSD_U32_BE: if (d->channels == 1) { if (d->lsb_first) { while (count--) { *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16 | dsd2pcm_bitreverse[*(iptrl+2)] << 8 | dsd2pcm_bitreverse[*(iptrl+3)]; *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16 | dsd2pcm_bitreverse[*(iptrl+2)] << 8 | dsd2pcm_bitreverse[*(iptrl+3)]; iptrl += 4; } } else { while (count--) { *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16 | *(iptrl+2) << 8 | *(iptrl+3); *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16 | *(iptrl+2) << 8 | *(iptrl+3); iptrl += 4; } } } else { if (d->lsb_first) { while (count--) { *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16 | dsd2pcm_bitreverse[*(iptrl+2)] << 8 | dsd2pcm_bitreverse[*(iptrl+3)]; *(optr++) = dsd2pcm_bitreverse[*(iptrr)] << 24 | dsd2pcm_bitreverse[*(iptrr+1)] << 16 | dsd2pcm_bitreverse[*(iptrr+2)] << 8 | dsd2pcm_bitreverse[*(iptrr+3)]; iptrl += 4; iptrr += 4; } } else { while (count--) { *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16 | *(iptrl+2) << 8 | *(iptrl+3); *(optr++) = *(iptrr) << 24 | *(iptrr+1) << 16 | *(iptrr+2) << 8 | *(iptrr+3); iptrl += 4; iptrr += 4; } } } break; case DSD_U16_LE: case DSD_U16_BE: if (d->channels == 1) { if (d->lsb_first) { while (count--) { *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16; *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16; iptrl += 2; } } else { while (count--) { *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16; *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16; iptrl += 2; } } } else { if (d->lsb_first) { while (count--) { *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16; *(optr++) = dsd2pcm_bitreverse[*(iptrr)] << 24 | dsd2pcm_bitreverse[*(iptrr+1)] << 16; iptrl += 2; iptrr += 2; } } else { while (count--) { *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16; *(optr++) = *(iptrr) << 24 | *(iptrr+1) << 16; iptrl += 2; iptrr += 2; } } } break; case DSD_U8: if (d->channels == 1) { if (d->lsb_first) { while (count--) { *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24; *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24; iptrl += 1; } } else { while (count--) { *(optr++) = *(iptrl) << 24; *(optr++) = *(iptrl) << 24; iptrl += 1; } } } else { if (d->lsb_first) { while (count--) { *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24; *(optr++) = dsd2pcm_bitreverse[*(iptrr)] << 24; iptrl += 1; iptrr += 1; } } else { while (count--) { *(optr++) = *(iptrl) << 24; *(optr++) = *(iptrr) << 24; iptrl += 1; iptrr += 1; } } } break; case DOP: case DOP_S24_LE: case DOP_S24_3LE: if (d->channels == 1) { if (d->lsb_first) { while (count--) { *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 16 | dsd2pcm_bitreverse[*(iptrl+1)] << 8; *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 16 | dsd2pcm_bitreverse[*(iptrl+1)] << 8; iptrl += 2; } } else { while (count--) { *(optr++) = *(iptrl) << 16 | *(iptrl+1) << 8; *(optr++) = *(iptrl) << 16 | *(iptrl+1) << 8; iptrl += 2; } } } else { if (d->lsb_first) { while (count--) { *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 16 | dsd2pcm_bitreverse[*(iptrl+1)] << 8; *(optr++) = dsd2pcm_bitreverse[*(iptrr)] << 16 | dsd2pcm_bitreverse[*(iptrr+1)] << 8; iptrl += 2; iptrr += 2; } } else { while (count--) { *(optr++) = *(iptrl) << 16 | *(iptrl+1) << 8; *(optr++) = *(iptrr) << 16 | *(iptrr+1) << 8; iptrl += 2; iptrr += 2; } } } break; case PCM: if (d->channels == 1) { float *iptrf = d->transfer[0]; dsd2pcm_translate(d->dsd2pcm_ctx[0], frames, iptrl, 1, d->lsb_first, iptrf, 1); while (count--) { double scaled = *iptrf++ * 0x7fffffff; if (scaled > 2147483647.0) scaled = 2147483647.0; if (scaled < -2147483648.0) scaled = -2147483648.0; *optr++ = (s32_t)scaled; *optr++ = (s32_t)scaled; } } else { float *iptrfl = d->transfer[0]; float *iptrfr = d->transfer[1]; dsd2pcm_translate(d->dsd2pcm_ctx[0], frames, iptrl, 1, d->lsb_first, iptrfl, 1); dsd2pcm_translate(d->dsd2pcm_ctx[1], frames, iptrr, 1, d->lsb_first, iptrfr, 1); while (count--) { double scaledl = *iptrfl++ * 0x7fffffff; double scaledr = *iptrfr++ * 0x7fffffff; if (scaledl > 2147483647.0) scaledl = 2147483647.0; if (scaledl < -2147483648.0) scaledl = -2147483648.0; if (scaledr > 2147483647.0) scaledr = 2147483647.0; if (scaledr < -2147483648.0) scaledr = -2147483648.0; *optr++ = (s32_t)scaledl; *optr++ = (s32_t)scaledr; } } break; } _buf_inc_readp(streambuf, bytes_read); block_left -= bytes_read; if (d->sample_bytes > bytes_read) { d->sample_bytes -= bytes_read; } else { LOG_INFO("end of track samples"); block_left = 0; d->sample_bytes = 0; } IF_DIRECT( _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames += frames; ); LOG_SDEBUG("write %u frames", frames); } if (padding) { _buf_inc_readp(streambuf, padding); LOG_INFO("Zero padding removed: %u bytes", padding); } // skip the other channel blocks // the right channel has already been read and is guarenteed to be in streambuf so can be skipped immediately if (d->channels > 1) { _buf_inc_readp(streambuf, d->block_size); } if (d->channels > 2) { d->consume = d->block_size * (d->channels - 2); } return DECODE_RUNNING; } static decode_state _decode_dsdiff(void) { // samples in streambuf are interleaved on byte per channel // we process as little as necessary per call and only need to handle frames wrapping round streambuf unsigned bytes_per_frame, bytes_read; frames_t out, frames, count; u8_t *iptr; u32_t *optr; u8_t tmp[WRAP_BUF_SIZE]; unsigned bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); IF_DIRECT( out = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; ); IF_PROCESS( out = process.max_in_frames; ); switch (outfmt) { case DSD_U32_LE: case DSD_U32_BE: bytes_per_frame = d->channels * 4; break; case DSD_U16_LE: case DSD_U16_BE: case DOP: case DOP_S24_LE: case DOP_S24_3LE: bytes_per_frame = d->channels * 2; break; default: bytes_per_frame = d->channels; out = min(out, BLOCK); } frames = min(min(bytes, d->sample_bytes) / bytes_per_frame, out); bytes_read = frames * bytes_per_frame; iptr = (u8_t *)streambuf->readp; IF_DIRECT( optr = (u32_t *)outputbuf->writep; ); IF_PROCESS( optr = (u32_t *)process.inbuf; ); // handle wrap around end of streambuf and partial dsd frame at end of stream if (!frames && bytes < bytes_per_frame) { memset(tmp, 0x69, WRAP_BUF_SIZE); // 0x69 = dsd silence memcpy(tmp, streambuf->readp, bytes); if (_buf_used(streambuf) > bytes_per_frame) { memcpy(tmp + bytes, streambuf->buf, bytes_per_frame - bytes); bytes_read = bytes_per_frame; } else { bytes_read = bytes; } iptr = tmp; frames = 1; } count = frames; switch (outfmt) { case DSD_U32_LE: case DSD_U32_BE: if (d->channels == 1) { while (count--) { *(optr++) = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3); *(optr++) = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3); iptr += bytes_per_frame; } } else { while (count--) { *(optr++) = *(iptr ) << 24 | *(iptr + d->channels) << 16 | *(iptr + 2 * d->channels) << 8 | *(iptr + 3 * d->channels); *(optr++) = *(iptr+1) << 24 | *(iptr + d->channels + 1) << 16 | *(iptr + 2 * d->channels + 1) << 8 | *(iptr + 3 * d->channels + 1); iptr += bytes_per_frame; } } break; case DSD_U16_LE: case DSD_U16_BE: if (d->channels == 1) { while (count--) { *(optr++) = *(iptr) << 24 | *(iptr+1) << 16; *(optr++) = *(iptr) << 24 | *(iptr+1) << 16; iptr += bytes_per_frame; } } else { while (count--) { *(optr++) = *(iptr ) << 24 | *(iptr + d->channels) << 16; *(optr++) = *(iptr+1) << 24 | *(iptr + d->channels + 1) << 16; iptr += bytes_per_frame; } } break; case DSD_U8: if (d->channels == 1) { while (count--) { *(optr++) = *(iptr) << 24; *(optr++) = *(iptr) << 24; iptr += bytes_per_frame; } } else { while (count--) { *(optr++) = *(iptr ) << 24; *(optr++) = *(iptr+1) << 24; iptr += bytes_per_frame; } } break; case DOP: case DOP_S24_LE: case DOP_S24_3LE: if (d->channels == 1) { while (count--) { *(optr++) = *(iptr) << 16 | *(iptr+1) << 8; *(optr++) = *(iptr) << 16 | *(iptr+1) << 8; iptr += bytes_per_frame; } } else { while (count--) { *(optr++) = *(iptr ) << 16 | *(iptr + d->channels) << 8; *(optr++) = *(iptr+1) << 16 | *(iptr + d->channels + 1) << 8; iptr += bytes_per_frame; } } break; case PCM: if (d->channels == 1) { float *iptrf = d->transfer[0]; dsd2pcm_translate(d->dsd2pcm_ctx[0], frames, iptr, 1, 0, iptrf, 1); while (count--) { double scaled = *iptrf++ * 0x7fffffff; if (scaled > 2147483647.0) scaled = 2147483647.0; if (scaled < -2147483648.0) scaled = -2147483648.0; *optr++ = (s32_t)scaled; *optr++ = (s32_t)scaled; } } else { float *iptrfl = d->transfer[0]; float *iptrfr = d->transfer[1]; dsd2pcm_translate(d->dsd2pcm_ctx[0], frames, iptr, d->channels, 0, iptrfl, 1); dsd2pcm_translate(d->dsd2pcm_ctx[1], frames, iptr + 1, d->channels, 0, iptrfr, 1); while (count--) { double scaledl = *iptrfl++ * 0x7fffffff; double scaledr = *iptrfr++ * 0x7fffffff; if (scaledl > 2147483647.0) scaledl = 2147483647.0; if (scaledl < -2147483648.0) scaledl = -2147483648.0; if (scaledr > 2147483647.0) scaledr = 2147483647.0; if (scaledr < -2147483648.0) scaledr = -2147483648.0; *optr++ = (s32_t)scaledl; *optr++ = (s32_t)scaledr; } } break; } _buf_inc_readp(streambuf, bytes_read); if (d->sample_bytes > bytes_read) { d->sample_bytes -= bytes_read; } else { LOG_INFO("end of track samples"); d->sample_bytes = 0; } IF_DIRECT( _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames = frames; ); LOG_SDEBUG("write %u frames", frames); return DECODE_RUNNING; } static decode_state dsd_decode(void) { decode_state ret; char *fmtstr; fmtstr = "None"; LOCK_S; if ((stream.state <= DISCONNECT && !_buf_used(streambuf)) || (!decode.new_stream && d->sample_bytes == 0)) { UNLOCK_S; return DECODE_COMPLETE; } if (d->consume) { unsigned consume = min(d->consume, min(_buf_used(streambuf), _buf_cont_read(streambuf))); LOG_DEBUG("consume: %u of %u", consume, d->consume); _buf_inc_readp(streambuf, consume); d->consume -= consume; if (d->consume) { UNLOCK_S; return DECODE_RUNNING; } } if (decode.new_stream) { int r = _read_header(); if (r < 1) { UNLOCK_S; return DECODE_ERROR; } if (r == 0) { UNLOCK_S; return DECODE_RUNNING; } // otherwise got to start of audio LOCK_O; LOG_INFO("setting track_start"); output.track_start = outputbuf->writep; outfmt = output.dsdfmt; switch (outfmt) { case DSD_U32_LE: fmtstr = "DSD_U32_LE"; output.next_sample_rate = d->sample_rate / 32; break; case DSD_U32_BE: fmtstr = "DSD_U32_BE"; output.next_sample_rate = d->sample_rate / 32; break; case DSD_U16_LE: fmtstr = "DSD_U16_LE"; output.next_sample_rate = d->sample_rate / 16; break; case DSD_U16_BE: fmtstr = "DSD_U16_BE"; output.next_sample_rate = d->sample_rate / 16; break; case DSD_U8: fmtstr = "DSD_U8"; output.next_sample_rate = d->sample_rate / 8; break; case DOP: fmtstr = "DOP"; output.next_sample_rate = d->sample_rate / 16; break; case DOP_S24_LE: fmtstr = "DOP_S24_LE"; output.next_sample_rate = d->sample_rate / 16; break; case DOP_S24_3LE: fmtstr = "DOP_S24_3LE"; output.next_sample_rate = d->sample_rate / 16; break; case PCM: // PCM case after DSD rate check and possible fallback to PCM conversion break; } if (outfmt != PCM && output.next_sample_rate > output.supported_rates[0]) { LOG_INFO("DSD sample rate too high for device - converting to PCM"); outfmt = PCM; } if (outfmt == PCM) { LOG_INFO("DSD to PCM output"); output.next_sample_rate = decode_newstream(d->sample_rate / 8, output.supported_rates); if (output.fade_mode) _checkfade(true); } else { LOG_INFO("DSD%u stream, format: %s, rate: %uHz\n", d->sample_rate / 44100, fmtstr, output.next_sample_rate); output.fade = FADE_INACTIVE; } output.next_fmt = outfmt; decode.new_stream = false; UNLOCK_O; } LOCK_O_direct; switch (d->type) { case DSF: ret = _decode_dsf(); break; case DSDIFF: ret = _decode_dsdiff(); break; default: ret = DECODE_ERROR; } UNLOCK_O_direct; UNLOCK_S; return ret; } void dsd_init(dsd_format format, unsigned delay) { LOCK_O; output.dsdfmt = format; output.dsd_delay = delay; UNLOCK_O; } static void dsd_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { d->type = UNKNOWN; if (!d->dsd2pcm_ctx[0]) { d->dsd2pcm_ctx[0] = dsd2pcm_init(); d->dsd2pcm_ctx[1] = dsd2pcm_init(); } else { dsd2pcm_reset(d->dsd2pcm_ctx[0]); dsd2pcm_reset(d->dsd2pcm_ctx[1]); } if (!d->transfer[1]) { d->transfer[0] = malloc(sizeof(float) * BLOCK); d->transfer[1] = malloc(sizeof(float) * BLOCK); } } static void dsd_close(void) { if (d->dsd2pcm_ctx[0]) { dsd2pcm_destroy(d->dsd2pcm_ctx[0]); dsd2pcm_destroy(d->dsd2pcm_ctx[1]); d->dsd2pcm_ctx[0] = NULL; d->dsd2pcm_ctx[1] = NULL; } if (d->transfer[0]) { free(d->transfer[0]); free(d->transfer[1]); d->transfer[0] = NULL; d->transfer[1] = NULL; } } struct codec *register_dsd(void) { static struct codec ret = { 'd', // id "dsf,dff", // types BLOCK * 2, // min read BLOCK_FRAMES,// min space dsd_open, // open dsd_close, // close dsd_decode, // decode }; d = malloc(sizeof(struct dsd)); if (!d) { return NULL; } memset(d, 0, sizeof(struct dsd)); dsd2pcm_precalc(); LOG_INFO("using dsd to decode dsf,dff"); return &ret; } // invert polarity for frames in the output buffer void dsd_invert(u32_t *ptr, frames_t frames) { while (frames--) { *ptr = ~(*ptr); ++ptr; *ptr = ~(*ptr); ++ptr; } } // fill silence buffer with 10101100 which represents dsd silence void dsd_silence_frames(u32_t *ptr, frames_t frames) { while (frames--) { *ptr++ = 0x69696969; *ptr++ = 0x69696969; } } #endif // DSD squeezelite-1.9.9-1395+git20220104.874e4f9/dsd2pcm/000077500000000000000000000000001416534420200204305ustar00rootroot00000000000000squeezelite-1.9.9-1395+git20220104.874e4f9/dsd2pcm/LICENSE.txt000066400000000000000000000027541416534420200222630ustar00rootroot00000000000000Copyright 2009, 2011 Sebastian Gesemann. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY SEBASTIAN GESEMANN ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN GESEMANN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Sebastian Gesemann. squeezelite-1.9.9-1395+git20220104.874e4f9/dsd2pcm/dsd2pcm.c000066400000000000000000000137431416534420200221400ustar00rootroot00000000000000/* Copyright 2009, 2011 Sebastian Gesemann. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY SEBASTIAN GESEMANN ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN GESEMANN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Sebastian Gesemann. ---- Additions (c) Adrian Smith, 2013 under same licence terms: - expose bitreverse array as dsd2pcm_bitreverse - expose precalc function as dsd2pcm_precalc to allow it to be initalised */ #include #include #include "dsd2pcm.h" #define HTAPS 48 /* number of FIR constants */ #define FIFOSIZE 16 /* must be a power of two */ #define FIFOMASK (FIFOSIZE-1) /* bit mask for FIFO offsets */ #define CTABLES ((HTAPS+7)/8) /* number of "8 MACs" lookup tables */ #if FIFOSIZE*8 < HTAPS*2 #error "FIFOSIZE too small" #endif /* * Properties of this 96-tap lowpass filter when applied on a signal * with sampling rate of 44100*64 Hz: * * () has a delay of 17 microseconds. * * () flat response up to 48 kHz * * () if you downsample afterwards by a factor of 8, the * spectrum below 70 kHz is practically alias-free. * * () stopband rejection is about 160 dB * * The coefficient tables ("ctables") take only 6 Kibi Bytes and * should fit into a modern processor's fast cache. */ /* * The 2nd half (48 coeffs) of a 96-tap symmetric lowpass filter */ static const double htaps[HTAPS] = { 0.09950731974056658, 0.09562845727714668, 0.08819647126516944, 0.07782552527068175, 0.06534876523171299, 0.05172629311427257, 0.0379429484910187, 0.02490921351762261, 0.0133774746265897, 0.003883043418804416, -0.003284703416210726, -0.008080250212687497, -0.01067241812471033, -0.01139427235000863, -0.0106813877974587, -0.009007905078766049, -0.006828859761015335, -0.004535184322001496, -0.002425035959059578, -0.0006922187080790708, 0.0005700762133516592, 0.001353838005269448, 0.001713709169690937, 0.001742046839472948, 0.001545601648013235, 0.001226696225277855, 0.0008704322683580222, 0.0005381636200535649, 0.000266446345425276, 7.002968738383528e-05, -5.279407053811266e-05, -0.0001140625650874684, -0.0001304796361231895, -0.0001189970287491285, -9.396247155265073e-05, -6.577634378272832e-05, -4.07492895872535e-05, -2.17407957554587e-05, -9.163058931391722e-06, -2.017460145032201e-06, 1.249721855219005e-06, 2.166655190537392e-06, 1.930520892991082e-06, 1.319400334374195e-06, 7.410039764949091e-07, 3.423230509967409e-07, 1.244182214744588e-07, 3.130441005359396e-08 }; static float ctables[CTABLES][256]; unsigned char dsd2pcm_bitreverse[256]; static int precalculated = 0; void dsd2pcm_precalc(void) { int t, e, m, k; double acc; if (precalculated) return; for (t=0, e=0; t<256; ++t) { dsd2pcm_bitreverse[t] = e; for (m=128; m && !((e^=m)&m); m>>=1) ; } for (t=0; t8) k=8; for (e=0; e<256; ++e) { acc = 0.0; for (m=0; m> (7-m)) & 1)*2-1) * htaps[t*8+m]; } ctables[CTABLES-1-t][e] = (float)acc; } } precalculated = 1; } struct dsd2pcm_ctx_s { unsigned char fifo[FIFOSIZE]; unsigned fifopos; }; extern dsd2pcm_ctx* dsd2pcm_init() { dsd2pcm_ctx* ptr; if (!precalculated) dsd2pcm_precalc(); ptr = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx)); if (ptr) dsd2pcm_reset(ptr); return ptr; } extern void dsd2pcm_destroy(dsd2pcm_ctx* ptr) { free(ptr); } extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx* ptr) { dsd2pcm_ctx* p2; p2 = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx)); if (p2) { memcpy(p2,ptr,sizeof(dsd2pcm_ctx)); } return p2; } extern void dsd2pcm_reset(dsd2pcm_ctx* ptr) { int i; for (i=0; ififo[i] = 0x69; /* my favorite silence pattern */ ptr->fifopos = 0; /* 0x69 = 01101001 * This pattern "on repeat" makes a low energy 352.8 kHz tone * and a high energy 1.0584 MHz tone which should be filtered * out completely by any playback system --> silence */ } extern void dsd2pcm_translate( dsd2pcm_ctx* ptr, size_t samples, const unsigned char *src, ptrdiff_t src_stride, int lsbf, float *dst, ptrdiff_t dst_stride) { unsigned ffp; unsigned i; unsigned bite1, bite2; unsigned char* p; double acc; ffp = ptr->fifopos; lsbf = lsbf ? 1 : 0; while (samples-- > 0) { bite1 = *src & 0xFFu; if (lsbf) bite1 = dsd2pcm_bitreverse[bite1]; ptr->fifo[ffp] = bite1; src += src_stride; p = ptr->fifo + ((ffp-CTABLES) & FIFOMASK); *p = dsd2pcm_bitreverse[*p & 0xFF]; acc = 0; for (i=0; ififo[(ffp -i) & FIFOMASK] & 0xFF; bite2 = ptr->fifo[(ffp-(CTABLES*2-1)+i) & FIFOMASK] & 0xFF; acc += ctables[i][bite1] + ctables[i][bite2]; } *dst = (float)acc; dst += dst_stride; ffp = (ffp + 1) & FIFOMASK; } ptr->fifopos = ffp; } squeezelite-1.9.9-1395+git20220104.874e4f9/dsd2pcm/dsd2pcm.h000066400000000000000000000064721416534420200221460ustar00rootroot00000000000000/* Copyright 2009, 2011 Sebastian Gesemann. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY SEBASTIAN GESEMANN ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN GESEMANN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of Sebastian Gesemann. ---- Marked additions (c) Adrian Smith, 2013 under same licence terms */ #ifndef DSD2PCM_H_INCLUDED #define DSD2PCM_H_INCLUDED #include #include #ifdef __cplusplus extern "C" { #endif struct dsd2pcm_ctx_s; typedef struct dsd2pcm_ctx_s dsd2pcm_ctx; /** * initializes a "dsd2pcm engine" for one channel * (precomputes tables and allocates memory) * * This is the only function that is not thread-safe in terms of the * POSIX thread-safety definition because it modifies global state * (lookup tables are computed during the first call) */ extern dsd2pcm_ctx* dsd2pcm_init(void); /** * deinitializes a "dsd2pcm engine" * (releases memory, don't forget!) */ extern void dsd2pcm_destroy(dsd2pcm_ctx *ctx); /** * clones the context and returns a pointer to the * newly allocated copy */ extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx *ctx); /** * resets the internal state for a fresh new stream */ extern void dsd2pcm_reset(dsd2pcm_ctx *ctx); /** * "translates" a stream of octets to a stream of floats * (8:1 decimation) * @param ctx -- pointer to abstract context (buffers) * @param samples -- number of octets/samples to "translate" * @param src -- pointer to first octet (input) * @param src_stride -- src pointer increment * @param lsbitfirst -- bitorder, 0=msb first, 1=lsbfirst * @param dst -- pointer to first float (output) * @param dst_stride -- dst pointer increment */ extern void dsd2pcm_translate(dsd2pcm_ctx *ctx, size_t samples, const unsigned char *src, ptrdiff_t src_stride, int lsbitfirst, float *dst, ptrdiff_t dst_stride); /** * Additions by Adrian Smith (c) 2013 for Squeezelite */ extern unsigned char dsd2pcm_bitreverse[]; extern void dsd2pcm_precalc(void); /** * End of addition */ #ifdef __cplusplus } /* extern "C" */ #endif #endif /* include guard DSD2PCM_H_INCLUDED */ squeezelite-1.9.9-1395+git20220104.874e4f9/faad.c000066400000000000000000000415441416534420200201430ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #include #if BYTES_PER_FRAME == 4 #define ALIGN(n) (n) #else #define ALIGN(n) (n << 8) #endif #define WRAPBUF_LEN 2048 struct chunk_table { u32_t sample, offset; }; struct faad { NeAACDecHandle hAac; u8_t type; // following used for mp4 only u32_t consume; u32_t pos; u32_t sample; u32_t nextchunk; void *stsc; u32_t skip; u64_t samples; u64_t sttssamples; bool empty; struct chunk_table *chunkinfo; // faad symbols to be dynamically loaded #if !LINKALL NeAACDecConfigurationPtr (* NeAACDecGetCurrentConfiguration)(NeAACDecHandle); unsigned char (* NeAACDecSetConfiguration)(NeAACDecHandle, NeAACDecConfigurationPtr); NeAACDecHandle (* NeAACDecOpen)(void); void (* NeAACDecClose)(NeAACDecHandle); long (* NeAACDecInit)(NeAACDecHandle, unsigned char *, unsigned long, unsigned long *, unsigned char *); char (* NeAACDecInit2)(NeAACDecHandle, unsigned char *pBuffer, unsigned long, unsigned long *, unsigned char *); void *(* NeAACDecDecode)(NeAACDecHandle, NeAACDecFrameInfo *, unsigned char *, unsigned long); char *(* NeAACDecGetErrorMessage)(unsigned char); #endif }; static struct faad *a; extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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 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 IF_DIRECT(x) { x } #define IF_PROCESS(x) #endif #if LINKALL #define NEAAC(h, fn, ...) (NeAACDec ## fn)(__VA_ARGS__) #else #define NEAAC(h, fn, ...) (h)->NeAACDec##fn(__VA_ARGS__) #endif // minimal code for mp4 file parsing to extract audio config and find media data // adapted from faad2/common/mp4ff u32_t mp4_desc_length(u8_t **buf) { u8_t b; u8_t num_bytes = 0; u32_t length = 0; do { b = **buf; *buf += 1; num_bytes++; length = (length << 7) | (b & 0x7f); } while ((b & 0x80) && num_bytes < 4); return length; } // read mp4 header to extract config data static int read_mp4_header(unsigned long *samplerate_p, unsigned char *channels_p) { size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); char type[5]; u32_t len; while (bytes >= 8) { // count trak to find the first playable one static unsigned trak, play; u32_t consume; len = unpackN((u32_t *)streambuf->readp); memcpy(type, streambuf->readp + 4, 4); type[4] = '\0'; if (!strcmp(type, "moov")) { trak = 0; play = 0; } if (!strcmp(type, "trak")) { trak++; } // extract audio config from within esds and pass to DecInit2 if (!strcmp(type, "esds") && bytes > len) { unsigned config_len; u8_t *ptr = streambuf->readp + 12; if (*ptr++ == 0x03) { mp4_desc_length(&ptr); ptr += 4; } else { ptr += 3; } mp4_desc_length(&ptr); ptr += 13; if (*ptr++ != 0x05) { LOG_WARN("error parsing esds"); return -1; } config_len = mp4_desc_length(&ptr); if (NEAAC(a, Init2, a->hAac, ptr, config_len, samplerate_p, channels_p) == 0) { LOG_DEBUG("playable aac track: %u", trak); play = trak; } } // extract the total number of samples from stts if (!strcmp(type, "stts") && bytes > len) { u32_t i; u8_t *ptr = streambuf->readp + 12; u32_t entries = unpackN((u32_t *)ptr); ptr += 4; for (i = 0; i < entries; ++i) { u32_t count = unpackN((u32_t *)ptr); u32_t size = unpackN((u32_t *)(ptr + 4)); a->sttssamples += count * size; ptr += 8; } LOG_DEBUG("total number of samples contained in stts: " FMT_u64, a->sttssamples); } // stash sample to chunk info, assume it comes before stco if (!strcmp(type, "stsc") && bytes > len && !a->chunkinfo) { a->stsc = malloc(len - 12); if (a->stsc == NULL) { LOG_WARN("malloc fail"); return -1; } memcpy(a->stsc, streambuf->readp + 12, len - 12); } // build offsets table from stco and stored stsc if (!strcmp(type, "stco") && bytes > len && play == trak) { u32_t i; // extract chunk offsets u8_t *ptr = streambuf->readp + 12; u32_t entries = unpackN((u32_t *)ptr); ptr += 4; a->chunkinfo = malloc(sizeof(struct chunk_table) * (entries + 1)); if (a->chunkinfo == NULL) { LOG_WARN("malloc fail"); return -1; } for (i = 0; i < entries; ++i) { a->chunkinfo[i].offset = unpackN((u32_t *)ptr); a->chunkinfo[i].sample = 0; ptr += 4; } a->chunkinfo[i].sample = 0; a->chunkinfo[i].offset = 0; // fill in first sample id for each chunk from stored stsc if (a->stsc) { u32_t stsc_entries = unpackN((u32_t *)a->stsc); u32_t sample = 0; u32_t last = 0, last_samples = 0; u8_t *ptr = (u8_t *)a->stsc + 4; while (stsc_entries--) { u32_t first = unpackN((u32_t *)ptr); u32_t samples = unpackN((u32_t *)(ptr + 4)); if (last) { for (i = last - 1; i < first - 1; ++i) { a->chunkinfo[i].sample = sample; sample += last_samples; } } if (stsc_entries == 0) { for (i = first - 1; i < entries; ++i) { a->chunkinfo[i].sample = sample; sample += samples; } } last = first; last_samples = samples; ptr += 12; } free(a->stsc); a->stsc = NULL; } } // found media data, advance to start of first chunk and return if (!strcmp(type, "mdat")) { _buf_inc_readp(streambuf, 8); a->pos += 8; bytes -= 8; if (play) { LOG_DEBUG("type: mdat len: %u pos: %u", len, a->pos); if (a->chunkinfo && a->chunkinfo[0].offset > a->pos) { u32_t skip = a->chunkinfo[0].offset - a->pos; LOG_DEBUG("skipping: %u", skip); if (skip <= bytes) { _buf_inc_readp(streambuf, skip); a->pos += skip; } else { a->consume = skip; } } a->sample = a->nextchunk = 1; return 1; } else { LOG_DEBUG("type: mdat len: %u, no playable track found", len); return -1; } } // parse key-value atoms within ilst ---- entries to get encoder padding within iTunSMPB entry for gapless if (!strcmp(type, "----") && bytes > len) { u8_t *ptr = streambuf->readp + 8; u32_t remain = len - 8, size; if (!memcmp(ptr + 4, "mean", 4) && (size = unpackN((u32_t *)ptr)) < remain) { ptr += size; remain -= size; } if (!memcmp(ptr + 4, "name", 4) && (size = unpackN((u32_t *)ptr)) < remain && !memcmp(ptr + 12, "iTunSMPB", 8)) { ptr += size; remain -= size; } if (!memcmp(ptr + 4, "data", 4) && remain > 16 + 48) { // data is stored as hex strings: 0 start end samples u32_t b, c; u64_t d; if (sscanf((const char *)(ptr + 16), "%x %x %x " FMT_x64, &b, &b, &c, &d) == 4) { LOG_DEBUG("iTunSMPB start: %u end: %u samples: " FMT_u64, b, c, d); if (a->sttssamples && a->sttssamples < b + c + d) { LOG_DEBUG("reducing samples as stts count is less"); d = a->sttssamples - (b + c); } a->skip = b; a->samples = d; } } } // default to consuming entire box consume = len; // read into these boxes so reduce consume if (!strcmp(type, "moov") || !strcmp(type, "trak") || !strcmp(type, "mdia") || !strcmp(type, "minf") || !strcmp(type, "stbl") || !strcmp(type, "udta") || !strcmp(type, "ilst")) { consume = 8; } // special cases which mix mix data in the enclosing box which we want to read into if (!strcmp(type, "stsd")) consume = 16; if (!strcmp(type, "mp4a")) consume = 36; if (!strcmp(type, "meta")) consume = 12; // consume rest of box if it has been parsed (all in the buffer) or is not one we want to parse if (bytes >= consume) { LOG_DEBUG("type: %s len: %u consume: %u", type, len, consume); _buf_inc_readp(streambuf, consume); a->pos += consume; bytes -= consume; } else if ( !(!strcmp(type, "esds") || !strcmp(type, "stts") || !strcmp(type, "stsc") || !strcmp(type, "stco") || !strcmp(type, "----")) ) { LOG_DEBUG("type: %s len: %u consume: %u - partial consume: %u", type, len, consume, bytes); _buf_inc_readp(streambuf, bytes); a->pos += bytes; a->consume = consume - bytes; break; } else if (len > streambuf->size) { // can't process an atom larger than streambuf! LOG_ERROR("atom %s too large for buffer %u %u", type, len, streambuf->size); return -1; } else { // make sure there is 'len' contiguous space _buf_unwrap(streambuf, len); break; } } return 0; } static decode_state faad_decode(void) { size_t bytes_total; size_t bytes_wrap; static NeAACDecFrameInfo info; ISAMPLE_T *iptr; bool endstream; frames_t frames; LOCK_S; bytes_total = _buf_used(streambuf); bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); if (stream.state <= DISCONNECT && !bytes_total) { UNLOCK_S; return DECODE_COMPLETE; } if (a->consume) { u32_t consume = min(a->consume, bytes_wrap); LOG_DEBUG("consume: %u of %u", consume, a->consume); _buf_inc_readp(streambuf, consume); a->pos += consume; a->consume -= consume; UNLOCK_S; return DECODE_RUNNING; } if (decode.new_stream) { int found = 0; static unsigned char channels; static unsigned long samplerate; if (a->type == '2') { // adts stream - seek for header while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) { _buf_inc_readp(streambuf, 1); bytes_total--; bytes_wrap--; } if (bytes_wrap >= 2) { long n = NEAAC(a, Init, a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels); if (n < 0) { found = -1; } else { _buf_inc_readp(streambuf, n); found = 1; } } } else { // mp4 - read header found = read_mp4_header(&samplerate, &channels); } if (found == 1) { LOG_INFO("samplerate: %u channels: %u", samplerate, channels); bytes_total = _buf_used(streambuf); bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); LOCK_O; LOG_INFO("setting track_start"); output.next_sample_rate = decode_newstream(samplerate, 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; } else if (found == -1) { LOG_WARN("error reading stream header"); UNLOCK_S; return DECODE_ERROR; } else { // not finished header parsing come back next time UNLOCK_S; return DECODE_RUNNING; } } if (bytes_wrap < WRAPBUF_LEN && bytes_total > WRAPBUF_LEN) { // make a local copy of frames which may have wrapped round the end of streambuf static u8_t buf[WRAPBUF_LEN]; memcpy(buf, streambuf->readp, bytes_wrap); memcpy(buf + bytes_wrap, streambuf->buf, WRAPBUF_LEN - bytes_wrap); iptr = NEAAC(a, Decode, a->hAac, &info, buf, WRAPBUF_LEN); } else { iptr = NEAAC(a, Decode, a->hAac, &info, streambuf->readp, bytes_wrap); } if (info.error) { LOG_WARN("error: %u %s", info.error, NEAAC(a, GetErrorMessage, info.error)); } endstream = false; // mp4 end of chunk - skip to next offset if (a->chunkinfo && a->chunkinfo[a->nextchunk].offset && a->sample++ == a->chunkinfo[a->nextchunk].sample) { if (a->chunkinfo[a->nextchunk].offset > a->pos) { u32_t skip = a->chunkinfo[a->nextchunk].offset - a->pos; if (skip != info.bytesconsumed) { LOG_DEBUG("skipping to next chunk pos: %u consumed: %u != skip: %u", a->pos, info.bytesconsumed, skip); } if (bytes_total >= skip) { _buf_inc_readp(streambuf, skip); a->pos += skip; } else { a->consume = skip; } a->nextchunk++; } else { LOG_ERROR("error: need to skip backwards!"); endstream = true; } // adts and mp4 when not at end of chunk } else if (info.bytesconsumed != 0) { _buf_inc_readp(streambuf, info.bytesconsumed); a->pos += info.bytesconsumed; // error which doesn't advance streambuf - end } else { endstream = true; } UNLOCK_S; if (endstream) { LOG_WARN("unable to decode further"); return DECODE_ERROR; } if (!info.samples) { a->empty = true; return DECODE_RUNNING; } frames = info.samples / info.channels; if (a->skip) { u32_t skip; if (a->empty) { a->empty = false; a->skip -= frames; LOG_DEBUG("gapless: first frame empty, skipped %u frames at start", frames); } skip = min(frames, a->skip); LOG_DEBUG("gapless: skipping %u frames at start", skip); frames -= skip; a->skip -= skip; iptr += skip * info.channels; } if (a->samples) { if (a->samples < frames) { LOG_DEBUG("gapless: trimming %u frames from end", frames - a->samples); frames = (frames_t)a->samples; } a->samples -= frames; } LOG_SDEBUG("write %u frames", frames); LOCK_O_direct; while (frames > 0) { frames_t f; frames_t count; ISAMPLE_T *optr; IF_DIRECT( f = _buf_cont_write(outputbuf) / BYTES_PER_FRAME; optr = (ISAMPLE_T *)outputbuf->writep; ); IF_PROCESS( f = process.max_in_frames; optr = (ISAMPLE_T *)process.inbuf; ); f = min(f, frames); count = f; if (info.channels == 2) { #if BYTES_PER_FRAME == 4 memcpy(optr, iptr, count * BYTES_PER_FRAME); iptr += count * 2; #else while (count--) { *optr++ = ALIGN(*iptr++); *optr++ = ALIGN(*iptr++); } #endif } else if (info.channels == 1) { while (count--) { *optr++ = ALIGN(*iptr); *optr++ = ALIGN(*iptr++); } } else { LOG_WARN("unsupported number of channels"); } frames -= f; IF_DIRECT( _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames = f; if (frames) LOG_ERROR("unhandled case"); ); } UNLOCK_O_direct; return DECODE_RUNNING; } static void faad_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { NeAACDecConfigurationPtr conf; LOG_INFO("opening %s stream", size == '2' ? "adts" : "mp4"); a->type = size; a->pos = a->consume = a->sample = a->nextchunk = 0; if (a->chunkinfo) { free(a->chunkinfo); } if (a->stsc) { free(a->stsc); } a->chunkinfo = NULL; a->stsc = NULL; a->skip = 0; a->samples = 0; a->sttssamples = 0; a->empty = false; if (a->hAac) { NEAAC(a, Close, a->hAac); } a->hAac = NEAAC(a, Open); conf = NEAAC(a, GetCurrentConfiguration, a->hAac); #if BYTES_PER_FRAME == 4 conf->outputFormat = FAAD_FMT_16BIT; #else conf->outputFormat = FAAD_FMT_24BIT; #endif conf->defSampleRate = 44100; conf->downMatrix = 1; if (!NEAAC(a, SetConfiguration, a->hAac, conf)) { LOG_WARN("error setting config"); }; } static void faad_close(void) { NEAAC(a, Close, a->hAac); a->hAac = NULL; if (a->chunkinfo) { free(a->chunkinfo); a->chunkinfo = NULL; } if (a->stsc) { free(a->stsc); a->stsc = NULL; } } static bool load_faad() { #if !LINKALL void *handle = dlopen(LIBFAAD, RTLD_NOW); char *err; if (!handle) { LOG_INFO("dlerror: %s", dlerror()); return false; } a->NeAACDecGetCurrentConfiguration = dlsym(handle, "NeAACDecGetCurrentConfiguration"); a->NeAACDecSetConfiguration = dlsym(handle, "NeAACDecSetConfiguration"); a->NeAACDecOpen = dlsym(handle, "NeAACDecOpen"); a->NeAACDecClose = dlsym(handle, "NeAACDecClose"); a->NeAACDecInit = dlsym(handle, "NeAACDecInit"); a->NeAACDecInit2 = dlsym(handle, "NeAACDecInit2"); a->NeAACDecDecode = dlsym(handle, "NeAACDecDecode"); a->NeAACDecGetErrorMessage = dlsym(handle, "NeAACDecGetErrorMessage"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBFAAD""); #endif return true; } struct codec *register_faad(void) { static struct codec ret = { 'a', // id "aac", // types WRAPBUF_LEN, // min read 20480, // min space faad_open, // open faad_close, // close faad_decode, // decode }; a = malloc(sizeof(struct faad)); if (!a) { return NULL; } a->hAac = NULL; a->chunkinfo = NULL; a->stsc = NULL; if (!load_faad()) { return NULL; } LOG_INFO("using faad to decode aac"); return &ret; } squeezelite-1.9.9-1395+git20220104.874e4f9/ffmpeg.c000066400000000000000000000567041416534420200205200ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #if FFMPEG #include #include #define READ_SIZE 4096 * 4 // this is large enough to ensure ffmpeg always gets new data when decode is called #define WRITE_SIZE 256 * 1024 // FIXME - make smaller, but still to absorb max wma output // FIXME - do we need to align these params as per ffmpeg on i386? #define attribute_align_arg struct ff_s { // state for ffmpeg decoder bool wma; u8_t wma_mmsh; u8_t wma_playstream; u8_t wma_metadatastream; u8_t *readbuf; bool end_of_stream; AVInputFormat *input_format; AVFormatContext *formatC; AVCodecContext *codecC; AVCodecParameters *codecP; AVFrame *frame; AVPacket *avpkt; unsigned mmsh_bytes_left; unsigned mmsh_bytes_pad; unsigned mmsh_packet_len; #if !LINKALL // ffmpeg symbols to be dynamically loaded from libavcodec unsigned (* avcodec_version)(void); AVCodec * (* avcodec_find_decoder)(int); int attribute_align_arg (* avcodec_open2)(AVCodecContext *, const AVCodec *, AVDictionary **); AVFrame * (* av_frame_alloc)(void); void (* av_frame_free)(AVFrame **); int attribute_align_arg (* avcodec_send_packet)(AVCodecContext *, const AVPacket *); int attribute_align_arg (* avcodec_receive_frame)(AVCodecContext *, AVFrame *); AVCodecContext * (* avcodec_alloc_context3)(const AVCodec *); void (* avcodec_free_context)(AVCodecContext **); int (* avcodec_parameters_to_context)(AVCodecContext *, const AVCodecParameters *); // ffmpeg symbols to be dynamically loaded from libavformat unsigned (* avformat_version)(void); AVFormatContext * (* avformat_alloc_context)(void); void (* avformat_free_context)(AVFormatContext *); int (* avformat_open_input)(AVFormatContext **, const char *, AVInputFormat *, AVDictionary **); int (* avformat_find_stream_info)(AVFormatContext *, AVDictionary **); AVIOContext * (* avio_alloc_context)(unsigned char *, int, int, void *, int (*read_packet)(void *, uint8_t *, int), int (*write_packet)(void *, uint8_t *, int), int64_t (*seek)(void *, int64_t, int)); AVPacket * (* av_packet_alloc)(void); #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58,133,100) void (* av_init_packet)(AVPacket *); #endif void (* av_packet_unref)(AVPacket *); int (* av_read_frame)(AVFormatContext *, AVPacket *); AVInputFormat * (* av_find_input_format)(const char *); #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) void (* av_register_all)(void); #endif // ffmpeg symbols to be dynamically loaded from libavutil unsigned (* avutil_version)(void); void (* av_log_set_callback)(void (*)(void*, int, const char*, va_list)); void (* av_log_set_level)(int); int (* av_strerror)(int, char *, size_t); void * (* av_malloc)(size_t); void (* av_freep)(void *); #endif }; static struct ff_s *ff; extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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 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 IF_DIRECT(x) { x } #define IF_PROCESS(x) #endif #if LINKALL #define AV(h, fn, ...) (av_ ## fn)(__VA_ARGS__) #define AVIO(h, fn, ...) (avio_ ## fn)(__VA_ARGS__) #define AVCODEC(h, fn, ...) (avcodec_ ## fn)(__VA_ARGS__) #define AVFORMAT(h, fn, ...) (avformat_ ## fn)(__VA_ARGS__) #else #define AV(h, fn, ...) (h)->av_##fn(__VA_ARGS__) #define AVIO(h, fn, ...) (h)->avio_##fn(__VA_ARGS__) #define AVCODEC(h, fn, ...) (h)->avcodec_##fn(__VA_ARGS__) #define AVFORMAT(h, fn, ...) (h)->avformat_##fn(__VA_ARGS__) #endif // our own version of useful error function not included in earlier ffmpeg versions static char *av__err2str(int errnum) { static char buf[64]; AV(ff, strerror, errnum, buf, 64); return buf; } // parser to extract asf data packet length from asf header const u8_t header_guid[16] = { 0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C }; const u8_t file_props_guid[16] = { 0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 }; static int _parse_packlen(void) { int bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); u8_t *ptr = streambuf->readp; int remain = 1; while (bytes >= 24 && remain > 0) { u32_t len = *(ptr+16) | *(ptr+17) << 8 | *(ptr+18) << 16 | *(ptr+19) << 24; // assume msb 32 bits are 0 if (!memcmp(ptr, header_guid, 16) && bytes >= 30) { ptr += 30; bytes -= 30; remain = len - 30; continue; } if (!memcmp(ptr, file_props_guid, 16) && len == 104) { u32_t packlen = *(ptr+92) | *(ptr+93) << 8 | *(ptr+94) << 16 | *(ptr+95) << 24; LOG_INFO("asf packet len: %u", packlen); return packlen; } ptr += len; bytes -= len; remain -= len; } LOG_WARN("could not parse packet length"); return 0; } static int _read_data(void *opaque, u8_t *buffer, int buf_size) { unsigned int bytes; LOCK_S; bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); ff->end_of_stream = (stream.state <= DISCONNECT && bytes == 0); bytes = min(bytes, buf_size); // for chunked wma extract asf header and data frames from framing structure // pad asf data frames to size of packet extracted from asf header if (ff->wma_mmsh) { unsigned chunk_type = 0, chunk_len = 0; if (ff->mmsh_bytes_left) { // bytes remaining from previous frame if (bytes >= ff->mmsh_bytes_left) { bytes = ff->mmsh_bytes_left; ff->mmsh_bytes_left = 0; } else { ff->mmsh_bytes_left -= bytes; } } else if (ff->mmsh_bytes_pad) { // add padding for previous frame bytes = min(ff->mmsh_bytes_pad, buf_size); memset(buffer, 0, bytes); ff->mmsh_bytes_pad -= bytes; UNLOCK_S; return bytes; } else if (bytes >= 12) { // new chunk header chunk_type = (*(streambuf->readp) & 0x7f) | *(streambuf->readp + 1) << 8; chunk_len = *(streambuf->readp + 2) | *(streambuf->readp + 3) << 8; _buf_inc_readp(streambuf, 12); bytes -= 12; } else if (_buf_used(streambuf) >= 12) { // new chunk header split over end of streambuf, read in two u8_t header[12]; memcpy(header, streambuf->readp, bytes); _buf_inc_readp(streambuf, bytes); memcpy(header + bytes, streambuf->readp, 12 - bytes); _buf_inc_readp(streambuf, 12 - bytes); chunk_type = (header[0] & 0x7f) | header[1] << 8; chunk_len = header[2] | header[3] << 8; bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); bytes = min(bytes, buf_size); } else { // should not get here... LOG_ERROR("chunk parser stalled bytes: %u %u", bytes, _buf_used(streambuf)); UNLOCK_S; return 0; } if (chunk_type && chunk_len) { if (chunk_type == 0x4824) { // asf header - parse packet length ff->mmsh_packet_len = _parse_packlen(); ff->mmsh_bytes_pad = 0; } else if (chunk_type == 0x4424 && ff->mmsh_packet_len) { // asf data packet - add padding ff->mmsh_bytes_pad = ff->mmsh_packet_len - chunk_len + 8; } else { LOG_INFO("unknown chunk: %04x", chunk_type); // other packet - no padding ff->mmsh_bytes_pad = 0; } if (chunk_len - 8 <= bytes) { bytes = chunk_len - 8; ff->mmsh_bytes_left = 0; } else { ff->mmsh_bytes_left = chunk_len - 8 - bytes; } } } memcpy(buffer, streambuf->readp, bytes); _buf_inc_readp(streambuf, bytes); if (ff->mmsh_bytes_pad && bytes + ff->mmsh_bytes_pad < buf_size) { memset(buffer + bytes, 0, ff->mmsh_bytes_pad); bytes += ff->mmsh_bytes_pad; ff->mmsh_bytes_pad = 0; } UNLOCK_S; return bytes; } static decode_state ff_decode(void) { int r; s32_t *optr = NULL; if (decode.new_stream) { AVIOContext *avio; AVStream *av_stream; AVCodec *codec; int o; int audio_stream = -1; ff->mmsh_bytes_left = ff->mmsh_bytes_pad = ff->mmsh_packet_len = 0; if (!ff->readbuf) { ff->readbuf = AV(ff, malloc, READ_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); } avio = AVIO(ff, alloc_context, ff->readbuf, READ_SIZE, 0, NULL, _read_data, NULL, NULL); avio->seekable = 0; ff->formatC = AVFORMAT(ff, alloc_context); if (ff->formatC == NULL) { LOG_ERROR("null context"); return DECODE_ERROR; } ff->formatC->pb = avio; ff->formatC->flags |= AVFMT_FLAG_CUSTOM_IO | AVFMT_FLAG_NOPARSE; o = AVFORMAT(ff, open_input, &ff->formatC, "", ff->input_format, NULL); if (o < 0) { LOG_WARN("avformat_open_input: %d %s", o, av__err2str(o)); return DECODE_ERROR; } LOG_INFO("format: name:%s lname:%s", ff->formatC->iformat->name, ff->formatC->iformat->long_name); o = AVFORMAT(ff, find_stream_info, ff->formatC, NULL); if (o < 0) { LOG_WARN("avformat_find_stream_info: %d %s", o, av__err2str(o)); return DECODE_ERROR; } if (ff->wma && ff->wma_playstream < ff->formatC->nb_streams) { if (ff->formatC->streams[ff->wma_playstream]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { LOG_INFO("using wma stream sent from server: %i", ff->wma_playstream); audio_stream = ff->wma_playstream; } } if (audio_stream == -1) { unsigned int i; for (i = 0; i < ff->formatC->nb_streams; ++i) { if (ff->formatC->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { audio_stream = i; LOG_INFO("found stream: %i", i); break; } } } if (audio_stream == -1) { LOG_WARN("no audio stream found"); return DECODE_ERROR; } av_stream = ff->formatC->streams[audio_stream]; ff->codecC = AVCODEC (ff, alloc_context3, NULL); if ( ff->codecC == NULL ) { LOG_ERROR("can't allocate avctx"); return DECODE_ERROR; } ff->codecP = av_stream->codecpar; if ( (AVCODEC(ff, parameters_to_context, ff->codecC, ff->codecP) ) < 0) { AVCODEC(ff, free_context, &ff->codecC); LOG_ERROR("can't initialize avctx"); return DECODE_ERROR; } codec = AVCODEC(ff, find_decoder, ff->codecP->codec_id); AVCODEC(ff, open2, ff->codecC, codec, NULL); ff->frame = AV(ff, frame_alloc); ff->avpkt = AV(ff, packet_alloc); if (ff->avpkt == NULL) { LOG_ERROR("can't allocate avpkt"); return DECODE_ERROR; } #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58,133,100) AV(ff, init_packet, ff->avpkt); // on older ffmpeg, this still is needed to e.g. set ->pos to -1 #endif LOCK_O; LOG_INFO("setting track_start"); output.next_sample_rate = decode_newstream(ff->codecP->sample_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; } if ((r = AV(ff, read_frame, ff->formatC, ff->avpkt)) < 0) { if (r == AVERROR_EOF) { if (ff->end_of_stream) { LOG_INFO("decode complete"); return DECODE_COMPLETE; } else { LOG_INFO("codec end of file"); } } else { LOG_ERROR("av_read_frame error: %i %s", r, av__err2str(r)); } return DECODE_RUNNING; } IF_PROCESS( optr = (s32_t *)process.inbuf; process.in_frames = 0; ); if ((r = AVCODEC(ff, send_packet, ff->codecC, ff->avpkt)) < 0) { AV(ff, packet_unref, ff->avpkt); if (r == AVERROR_EOF) { LOG_DEBUG("av_send_packet reports eof"); return DECODE_COMPLETE; } else { LOG_ERROR("av_send_packet error: %i %s", r, av__err2str(r)); // EAGAIN is treated as an unrecoverable error here since all pending output frames are processed below if (r == AVERROR(EAGAIN) || r == AVERROR(EINVAL) || r == AVERROR(ENOMEM)) return DECODE_ERROR; // retain existing behavior (continue decoding) for legitimate decoding errors return DECODE_RUNNING; } } else { // a single input packet can lead to multiple output frames - process all of them while ((r = AVCODEC(ff, receive_frame, ff->codecC, ff->frame)) >= 0) { s16_t *iptr16 = (s16_t *)ff->frame->data[0]; s32_t *iptr32 = (s32_t *)ff->frame->data[0]; s16_t *iptr16l = (s16_t *)ff->frame->data[0]; s16_t *iptr16r = (s16_t *)ff->frame->data[1]; s32_t *iptr32l = (s32_t *)ff->frame->data[0]; s32_t *iptr32r = (s32_t *)ff->frame->data[1]; float *iptrfl = (float *)ff->frame->data[0]; float *iptrfr = (float *)ff->frame->data[1]; frames_t frames = ff->frame->nb_samples; LOG_SDEBUG("got audio channels: %u samples: %u format: %u", ff->codecC->channels, ff->frame->nb_samples, ff->codecC->sample_fmt); LOCK_O_direct; while (frames > 0) { frames_t count; frames_t f; IF_DIRECT( optr = (s32_t *)outputbuf->writep; f = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; f = min(f, frames); ); IF_PROCESS( if (process.in_frames + frames > process.max_in_frames) { LOG_WARN("exceeded process buffer size - dropping frames"); break; } f = frames; ); count = f; if (ff->codecC->channels == 2) { if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S16) { while (count--) { *optr++ = *iptr16++ << 16; *optr++ = *iptr16++ << 16; } } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S32) { while (count--) { *optr++ = *iptr32++; *optr++ = *iptr32++; } } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S16P) { while (count--) { *optr++ = *iptr16l++ << 16; *optr++ = *iptr16r++ << 16; } } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S32P) { while (count--) { *optr++ = *iptr32l++; *optr++ = *iptr32r++; } } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_FLTP) { while (count--) { double scaledl = *iptrfl++ * 0x7fffffff; double scaledr = *iptrfr++ * 0x7fffffff; if (scaledl > 2147483647.0) scaledl = 2147483647.0; if (scaledl < -2147483648.0) scaledl = -2147483648.0; if (scaledr > 2147483647.0) scaledr = 2147483647.0; if (scaledr < -2147483648.0) scaledr = -2147483648.0; *optr++ = (s32_t)scaledl; *optr++ = (s32_t)scaledr; } } else { LOG_WARN("unsupported sample format: %u", ff->codecC->sample_fmt); } } else if (ff->codecC->channels == 1) { if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S16) { while (count--) { *optr++ = *iptr16 << 16; *optr++ = *iptr16++ << 16; } } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S32) { while (count--) { *optr++ = *iptr32; *optr++ = *iptr32++; } } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S16P) { while (count--) { *optr++ = *iptr16l << 16; *optr++ = *iptr16l++ << 16; } } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S32P) { while (count--) { *optr++ = *iptr32l; *optr++ = *iptr32l++; } } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_FLTP) { while (count--) { double scaled = *iptrfl++ * 0x7fffffff; if (scaled > 2147483647.0) scaled = 2147483647.0; if (scaled < -2147483648.0) scaled = -2147483648.0; *optr++ = (s32_t)scaled; *optr++ = (s32_t)scaled; } } else { LOG_WARN("unsupported sample format: %u", ff->codecC->sample_fmt); } } else { LOG_WARN("unsupported number of channels"); } frames -= f; IF_DIRECT( _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames += f; ); } UNLOCK_O_direct; } } AV(ff, packet_unref, ff->avpkt); if (r == AVERROR(EAGAIN)) { // EAGAIN is expected if there are no more pending frames } else if (r == AVERROR_EOF) { LOG_DEBUG("av_receive_frame reports eof"); return DECODE_COMPLETE; } else { LOG_ERROR("av_receive_frame error: %i %s", r, av__err2str(r)); if (r == AVERROR(EINVAL)) return DECODE_ERROR; // retain existing behavior (continue decoding) for legitimate decoding errors } return DECODE_RUNNING; } static void _free_ff_data(void) { if (ff->codecC) { AVCODEC(ff, free_context, &ff->codecC); ff->codecC = NULL; } if (ff->formatC) { if (ff->formatC->pb) { // per ffmpeg docs, the buffer originally pointed to by ff->readbuf may be dynamically freed and reallocated behind the scenes, so this is the one that must be freed // otherwise, a double free can occur (seen by valgrind), resulting in e.g. SIGILL AV(ff, freep, &ff->formatC->pb->buffer); ff->readbuf = NULL; AV(ff, freep, &ff->formatC->pb); } AVFORMAT(ff, free_context, ff->formatC); ff->formatC = NULL; } if (ff->frame) { AV(ff, frame_free, &ff->frame); ff->frame = NULL; } if (ff->avpkt) { AV(ff, packet_unref, ff->avpkt); AV(ff, freep, &ff->avpkt); ff->avpkt = NULL; } } static void ff_open_wma(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { _free_ff_data(); ff->input_format = AV(ff, find_input_format, "asf"); if (ff->input_format == NULL) { LOG_ERROR("asf format not supported by ffmpeg library"); } ff->wma = true; ff->wma_mmsh = size - '0'; ff->wma_playstream = rate - 1; ff->wma_metadatastream = chan != '?' ? chan : 0; LOG_INFO("open wma chunking: %u playstream: %u metadatastream: %u", ff->wma_mmsh, ff->wma_playstream, ff->wma_metadatastream); } static void ff_open_alac(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { _free_ff_data(); ff->input_format = AV(ff, find_input_format, "mp4"); if (ff->input_format == NULL) { LOG_ERROR("mp4 format not supported by ffmpeg library"); } ff->wma = false; ff->wma_mmsh = 0; LOG_INFO("open alac"); } static void ff_close(void) { _free_ff_data(); } static bool load_ff() { #if !LINKALL void *handle_codec = NULL, *handle_format = NULL, *handle_util = NULL; char name[30]; char *err; // we try to load the ffmpeg library version which matches the header file we are compiled with as structs differ between versions sprintf(name, LIBAVCODEC, LIBAVCODEC_VERSION_MAJOR); handle_codec = dlopen(name, RTLD_NOW); if (!handle_codec) { LOG_INFO("dlerror: %s", dlerror()); return false; } sprintf(name, LIBAVFORMAT, LIBAVFORMAT_VERSION_MAJOR); handle_format = dlopen(name, RTLD_NOW); if (!handle_format) { LOG_INFO("dlerror: %s", dlerror()); return false; } sprintf(name, LIBAVUTIL, LIBAVUTIL_VERSION_MAJOR); handle_util = dlopen(name, RTLD_NOW); if (!handle_util) { LOG_INFO("dlerror: %s", dlerror()); return false; } ff->avcodec_version = dlsym(handle_codec, "avcodec_version"); ff->avcodec_find_decoder = dlsym(handle_codec, "avcodec_find_decoder"); ff->avcodec_open2 = dlsym(handle_codec, "avcodec_open2"); ff->av_frame_alloc = dlsym(handle_codec, "av_frame_alloc"); ff->av_frame_free = dlsym(handle_codec, "av_frame_free"); ff->avcodec_send_packet = dlsym(handle_codec, "avcodec_send_packet"); ff->avcodec_receive_frame = dlsym(handle_codec, "avcodec_receive_frame"); ff->avcodec_alloc_context3 = dlsym(handle_format, "avcodec_alloc_context3"); ff->avcodec_free_context = dlsym(handle_format, "avcodec_free_context"); ff->avcodec_parameters_to_context = dlsym(handle_format, "avcodec_parameters_to_context"); ff->av_packet_alloc = dlsym(handle_codec, "av_packet_alloc"); #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58,133,100) ff->av_init_packet = dlsym(handle_codec, "av_init_packet"); #endif ff->av_packet_unref = dlsym(handle_codec, "av_packet_unref"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBAVCODEC" (%u.%u.%u)", LIBAVCODEC_VERSION_MAJOR, ff->avcodec_version() >> 16, (ff->avcodec_version() >> 8) & 0xff, ff->avcodec_version() & 0xff); ff->avformat_version = dlsym(handle_format, "avformat_version"); ff->avformat_alloc_context = dlsym(handle_format, "avformat_alloc_context"); ff->avformat_free_context = dlsym(handle_format, "avformat_free_context"); ff->avformat_open_input = dlsym(handle_format, "avformat_open_input"); ff->avformat_find_stream_info = dlsym(handle_format, "avformat_find_stream_info"); ff->avio_alloc_context = dlsym(handle_format, "avio_alloc_context"); ff->av_read_frame = dlsym(handle_format, "av_read_frame"); ff->av_find_input_format= dlsym(handle_format, "av_find_input_format"); #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) ff->av_register_all = dlsym(handle_format, "av_register_all"); #endif if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBAVFORMAT" (%u.%u.%u)", LIBAVFORMAT_VERSION_MAJOR, ff->avformat_version() >> 16, (ff->avformat_version() >> 8) & 0xff, ff->avformat_version() & 0xff); ff->avutil_version = dlsym(handle_util, "avutil_version"); ff->av_log_set_callback = dlsym(handle_util, "av_log_set_callback"); ff->av_log_set_level = dlsym(handle_util, "av_log_set_level"); ff->av_strerror = dlsym(handle_util, "av_strerror"); ff->av_malloc = dlsym(handle_util, "av_malloc"); ff->av_freep = dlsym(handle_util, "av_freep"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBAVUTIL" (%u.%u.%u)", LIBAVUTIL_VERSION_MAJOR, ff->avutil_version() >> 16, (ff->avutil_version() >> 8) & 0xff, ff->avutil_version() & 0xff); #endif return true; } static int ff_log_level = 0; void av_err_callback(void *avcl, int level, const char *fmt, va_list vl) { if (level > ff_log_level) return; fprintf(stderr, "%s ffmpeg: ", logtime()); vfprintf(stderr, fmt, vl); fflush(stderr); } static bool registered = false; struct codec *register_ff(const char *codec) { if (!registered) { ff = malloc(sizeof(struct ff_s)); if (!ff) { return NULL; } memset(ff, 0, sizeof(struct ff_s)); if (!load_ff()) { return NULL; } switch (loglevel) { case lERROR: ff_log_level = AV_LOG_ERROR; break; case lWARN: ff_log_level = AV_LOG_WARNING; break; case lINFO: ff_log_level = AV_LOG_INFO; break; case lDEBUG: ff_log_level = AV_LOG_VERBOSE; break; default: break; } AV(ff, log_set_callback, av_err_callback); #if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58,9,100) AV(ff, register_all); #endif registered = true; } if (!strcmp(codec, "wma")) { static struct codec ret = { 'w', // id "wma,wmap", // types READ_SIZE, // min read WRITE_SIZE, // min space ff_open_wma, // open ff_close, // close ff_decode, // decode }; LOG_INFO("using ffmpeg to decode wma,wmap"); return &ret; } if (!strcmp(codec, "alc")) { static struct codec ret = { 'l', // id "alc", // types READ_SIZE, // min read WRITE_SIZE, // min space ff_open_alac,// open ff_close, // close ff_decode, // decode }; LOG_INFO("using ffmpeg to decode alc"); return &ret; } return NULL; } #endif squeezelite-1.9.9-1395+git20220104.874e4f9/flac.c000066400000000000000000000237321416534420200201540ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezeplay emulator for linux * * (c) Adrian Smith 2012, triode1@btinternet.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #include #if BYTES_PER_FRAME == 4 #define ALIGN8(n) (n << 8) #define ALIGN16(n) (n) #define ALIGN24(n) (n >> 8) #define ALIGN32(n) (n >> 16) #else #define ALIGN8(n) (n << 24) #define ALIGN16(n) (n << 16) #define ALIGN24(n) (n << 8) #define ALIGN32(n) (n) #endif struct flac { FLAC__StreamDecoder *decoder; u8_t container; #if !LINKALL // FLAC symbols to be dynamically loaded const char **FLAC__StreamDecoderErrorStatusString; const char **FLAC__StreamDecoderStateString; FLAC__StreamDecoder * (* FLAC__stream_decoder_new)(void); FLAC__bool (* FLAC__stream_decoder_reset)(FLAC__StreamDecoder *decoder); void (* FLAC__stream_decoder_delete)(FLAC__StreamDecoder *decoder); FLAC__StreamDecoderInitStatus (* FLAC__stream_decoder_init_stream)( FLAC__StreamDecoder *decoder, FLAC__StreamDecoderReadCallback read_callback, FLAC__StreamDecoderSeekCallback seek_callback, FLAC__StreamDecoderTellCallback tell_callback, FLAC__StreamDecoderLengthCallback length_callback, FLAC__StreamDecoderEofCallback eof_callback, FLAC__StreamDecoderWriteCallback write_callback, FLAC__StreamDecoderMetadataCallback metadata_callback, FLAC__StreamDecoderErrorCallback error_callback, void *client_data ); FLAC__StreamDecoderInitStatus (* FLAC__stream_decoder_init_ogg_stream)( FLAC__StreamDecoder *decoder, FLAC__StreamDecoderReadCallback read_callback, FLAC__StreamDecoderSeekCallback seek_callback, FLAC__StreamDecoderTellCallback tell_callback, FLAC__StreamDecoderLengthCallback length_callback, FLAC__StreamDecoderEofCallback eof_callback, FLAC__StreamDecoderWriteCallback write_callback, FLAC__StreamDecoderMetadataCallback metadata_callback, FLAC__StreamDecoderErrorCallback error_callback, void *client_data ); FLAC__bool (* FLAC__stream_decoder_process_single)(FLAC__StreamDecoder *decoder); FLAC__StreamDecoderState (* FLAC__stream_decoder_get_state)(const FLAC__StreamDecoder *decoder); #endif }; static struct flac *f; extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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 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 IF_DIRECT(x) { x } #define IF_PROCESS(x) #endif #if LINKALL #define FLAC(h, fn, ...) (FLAC__ ## fn)(__VA_ARGS__) #define FLAC_A(h, a) (FLAC__ ## a) #else #define FLAC(h, fn, ...) (h)->FLAC__##fn(__VA_ARGS__) #define FLAC_A(h, a) (h)->FLAC__ ## a #endif static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *want, void *client_data) { size_t bytes; bool end; LOCK_S; bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); bytes = min(bytes, *want); end = (stream.state <= DISCONNECT && bytes == 0); memcpy(buffer, streambuf->readp, bytes); _buf_inc_readp(streambuf, bytes); UNLOCK_S; *want = bytes; // if there's nothing in the stream buffer, libFLAC will continuously call this function as quickly as possible. slow it down. if (!bytes && !end) usleep(1000); return end ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } static FLAC__StreamDecoderWriteStatus write_cb(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) { size_t frames = frame->header.blocksize; unsigned bits_per_sample = frame->header.bits_per_sample; unsigned channels = frame->header.channels; FLAC__int32 *lptr = (FLAC__int32 *)buffer[0]; FLAC__int32 *rptr = (FLAC__int32 *)buffer[channels > 1 ? 1 : 0]; if (decode.new_stream) { LOCK_O; LOG_INFO("setting track_start"); output.track_start = outputbuf->writep; decode.new_stream = false; #if DSD #if SL_LITTLE_ENDIAN #define MARKER_OFFSET 2 #else #define MARKER_OFFSET 1 #endif if (bits_per_sample == 24 && is_stream_dop(((u8_t *)lptr) + MARKER_OFFSET, ((u8_t *)rptr) + MARKER_OFFSET, 4, frames)) { LOG_INFO("file contains DOP"); if (output.dsdfmt == DOP_S24_LE || output.dsdfmt == DOP_S24_3LE) output.next_fmt = output.dsdfmt; else output.next_fmt = DOP; output.next_sample_rate = frame->header.sample_rate; output.fade = FADE_INACTIVE; } else { output.next_sample_rate = decode_newstream(frame->header.sample_rate, output.supported_rates); output.next_fmt = PCM; if (output.fade_mode) _checkfade(true); } #else output.next_sample_rate = decode_newstream(frame->header.sample_rate, output.supported_rates); if (output.fade_mode) _checkfade(true); #endif UNLOCK_O; } LOCK_O_direct; while (frames > 0) { frames_t f; frames_t count; ISAMPLE_T *optr; IF_DIRECT( optr = (ISAMPLE_T *)outputbuf->writep; f = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; ); IF_PROCESS( optr = (ISAMPLE_T *)process.inbuf; f = process.max_in_frames; ); f = min(f, frames); count = f; if (bits_per_sample == 8) { while (count--) { *optr++ = ALIGN8(*lptr++); *optr++ = ALIGN8(*rptr++); } } else if (bits_per_sample == 16) { while (count--) { *optr++ = ALIGN16(*lptr++); *optr++ = ALIGN16(*rptr++); } } else if (bits_per_sample == 24) { while (count--) { *optr++ = ALIGN24(*lptr++); *optr++ = ALIGN24(*rptr++); } } else if (bits_per_sample == 32) { while (count--) { *optr++ = ALIGN32(*lptr++); *optr++ = ALIGN32(*rptr++); } } else { LOG_ERROR("unsupported bits per sample: %u", bits_per_sample); } frames -= f; IF_DIRECT( _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames = f; if (frames) LOG_ERROR("unhandled case"); ); } UNLOCK_O_direct; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } static void error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { LOG_INFO("flac error: %s", FLAC_A(f, StreamDecoderErrorStatusString)[status]); } static void flac_close(void) { FLAC(f, stream_decoder_delete, f->decoder); f->decoder = NULL; } static void flac_open(u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness) { if ( f->decoder && f->container != sample_size ) { flac_close(); } f->container = sample_size; if (f->decoder) { FLAC(f, stream_decoder_reset, f->decoder); } else { f->decoder = FLAC(f, stream_decoder_new); } if ( f->container == 'o' ) { LOG_INFO("ogg/flac container - using init_ogg_stream"); FLAC(f, stream_decoder_init_ogg_stream, f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL); } else { FLAC(f, stream_decoder_init_stream, f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL); } } static decode_state flac_decode(void) { bool ok = FLAC(f, stream_decoder_process_single, f->decoder); FLAC__StreamDecoderState state = FLAC(f, stream_decoder_get_state, f->decoder); if (!ok && state != FLAC__STREAM_DECODER_END_OF_STREAM) { LOG_INFO("flac error: %s", FLAC_A(f, StreamDecoderStateString)[state]); }; if (state == FLAC__STREAM_DECODER_END_OF_STREAM) { return DECODE_COMPLETE; } else if (state > FLAC__STREAM_DECODER_END_OF_STREAM) { return DECODE_ERROR; } else { return DECODE_RUNNING; } } static bool load_flac() { #if !LINKALL void *handle = dlopen(LIBFLAC, RTLD_NOW); char *err; if (!handle) { LOG_INFO("dlerror: %s", dlerror()); return false; } f->FLAC__StreamDecoderErrorStatusString = dlsym(handle, "FLAC__StreamDecoderErrorStatusString"); f->FLAC__StreamDecoderStateString = dlsym(handle, "FLAC__StreamDecoderStateString"); f->FLAC__stream_decoder_new = dlsym(handle, "FLAC__stream_decoder_new"); f->FLAC__stream_decoder_reset = dlsym(handle, "FLAC__stream_decoder_reset"); f->FLAC__stream_decoder_delete = dlsym(handle, "FLAC__stream_decoder_delete"); f->FLAC__stream_decoder_init_stream = dlsym(handle, "FLAC__stream_decoder_init_stream"); f->FLAC__stream_decoder_init_ogg_stream = dlsym(handle, "FLAC__stream_decoder_init_ogg_stream"); f->FLAC__stream_decoder_process_single = dlsym(handle, "FLAC__stream_decoder_process_single"); f->FLAC__stream_decoder_get_state = dlsym(handle, "FLAC__stream_decoder_get_state"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBFLAC); #endif return true; } struct codec *register_flac(void) { static struct codec ret = { 'f', // id "ogf,flc", // types 16384, // min read 204800, // min space flac_open, // open flac_close, // close flac_decode, // decode }; f = malloc(sizeof(struct flac)); if (!f) { return NULL; } f->decoder = NULL; if (!load_flac()) { return NULL; } LOG_INFO("using flac to decode ogf,flc"); return &ret; } squeezelite-1.9.9-1395+git20220104.874e4f9/gpio.c000066400000000000000000000051431416534420200202010ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * gpio.c (c) Paul Hermann, 2015-2021 under the same license terms * -Control of Raspberry pi GPIO for amplifier power * -Launch script on power status change from LMS */ #if GPIO #include "squeezelite.h" #include #include #include static int gpio_state = -1; static int initialized = -1; static int power_state = -1; void relay( int state) { #ifdef RPI gpio_state = state; // Set up gpio using BCM Pin #'s if (initialized == -1){ if ( gpioInitialise() == 0 ){ initialized = 1; } } if ( initialized == 1){ gpioSetMode (gpio_pin, PI_OUTPUT); } if(gpio_state == 1) gpioWrite(gpio_pin, PI_HIGH^gpio_active_low); else if(gpio_state == 0) gpioWrite(gpio_pin, PI_LOW^gpio_active_low); // Done! #endif } char *cmdline; int argloc; void relay_script( int state) { gpio_state = state; int err; // Call script with init parameter if (initialized == -1){ int strsize = strlen(power_script); cmdline = (char*) malloc(strsize+3); argloc = strsize + 1; strcpy(cmdline, power_script); strcat(cmdline, " 2"); if ((err = system(cmdline)) != 0){ fprintf (stderr, "%s exit status = %d\n", cmdline, err); } else{ initialized = 1; } } // Call Script to turn on or off on = 1, off = 0 // Checks current status to avoid calling script excessivly on track changes where alsa re-inits. if( (gpio_state == 1) && (power_state != 1)){ cmdline[argloc] = '1'; if ((err = system(cmdline)) != 0){ fprintf (stderr, "%s exit status = %d\n", cmdline, err); } else { power_state = 1; } } else if( (gpio_state == 0) && (power_state != 0)){ cmdline[argloc] = '0'; if ((err = system(cmdline)) != 0){ fprintf (stderr, "%s exit status = %d\n", cmdline, err); } else { power_state = 0; } } // Done! } #endif // GPIO squeezelite-1.9.9-1395+git20220104.874e4f9/ir.c000066400000000000000000000167321416534420200176630ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // ir thread - linux only #if IR #include /* Avoid name conflicts from syslog.h inclusion in lirc client header */ #ifdef LOG_DEBUG #undef LOG_DEBUG #endif #ifdef LOG_INFO #undef LOG_INFO #endif #include "squeezelite.h" #define LIRC_CLIENT_ID "squeezelite" static log_level loglevel; struct irstate ir; static struct lirc_config *config = NULL; static sockfd fd = -1; static thread_type thread; #define LOCK_I mutex_lock(ir.mutex) #define UNLOCK_I mutex_unlock(ir.mutex) #if !LINKALL struct lirc { // LIRC symbols to be dynamically loaded int (* lirc_init)(char *prog, int verbose); int (* lirc_deinit)(void); int (* lirc_readconfig)(char *file, struct lirc_config **config, int (check) (char *s)); void (* lirc_freeconfig)(struct lirc_config *config); int (* lirc_nextcode)(char **code); int (* lirc_code2char)(struct lirc_config *config, char *code, char **string); }; static struct lirc *i; #endif #if LINKALL #define LIRC(h, fn, ...) (lirc_ ## fn)(__VA_ARGS__) #else #define LIRC(h, fn, ...) (h)->lirc_##fn(__VA_ARGS__) #endif // cmds based on entries in Slim_Device_Remote.ir // these may appear as config entries in .lircrc files static struct { char *cmd; u32_t code; } cmdmap[] = { { "voldown", 0x768900ff }, { "volup", 0x7689807f }, { "rew", 0x7689c03f }, { "fwd", 0x7689a05f }, { "pause", 0x768920df }, { "play", 0x768910ef }, { "power", 0x768940bf }, { "muting", 0x7689c43b }, { "power_on", 0x76898f70 }, { "power_off",0x76898778 }, { "preset_1", 0x76898a75 }, { "preset_2", 0x76894ab5 }, { "preset_3", 0x7689ca35 }, { "preset_4", 0x76892ad5 }, { "preset_5", 0x7689aa55 }, { "preset_6", 0x76896a95 }, { NULL, 0 }, }; // selected lirc namespace button names as defaults - some support repeat static struct { char *lirc; u32_t code; bool repeat; } keymap[] = { { "KEY_VOLUMEDOWN", 0x768900ff, true }, { "KEY_VOLUMEUP", 0x7689807f, true }, { "KEY_PREVIOUS", 0x7689c03f, true }, { "KEY_REWIND", 0x7689c03f, true }, { "KEY_NEXT", 0x7689a05f, true }, { "KEY_FORWARD", 0x7689a05f, true }, { "KEY_PAUSE", 0x768920df, true }, { "KEY_PLAY", 0x768910ef, false }, { "KEY_POWER", 0x768940bf, false }, { "KEY_MUTE", 0x7689c43b, false }, { "KEY_0", 0x76899867, true }, { "KEY_1", 0x7689f00f, true }, { "KEY_2", 0x768908f7, true }, { "KEY_3", 0x76898877, true }, { "KEY_4", 0x768948b7, true }, { "KEY_5", 0x7689c837, true }, { "KEY_6", 0x768928d7, true }, { "KEY_7", 0x7689a857, true }, { "KEY_8", 0x76896897, true }, { "KEY_9", 0x7689e817, true }, { "KEY_FAVORITES", 0x768918e7, false }, { "KEY_FAVORITES", 0x7689e21d, false }, { "KEY_SEARCH", 0x768958a7, false }, { "KEY_SEARCH", 0x7689629d, false }, { "KEY_SHUFFLE", 0x7689d827, false }, { "KEY_SLEEP", 0x7689b847, false }, { "KEY_INSERT", 0x7689609f, false }, // Add { "KEY_UP", 0x7689e01f, true }, { "KEY_LEFT", 0x7689906f, true }, { "KEY_RIGHT", 0x7689d02f, true }, { "KEY_DOWN", 0x7689b04f, true }, { "KEY_HOME", 0x768922dd, false }, { "KEY_MEDIA_REPEAT", 0x768938c7, false }, { "KEY_AGAIN", 0x768938c7, false }, // { "KEY_TITLE", 0x76897887, false }, // Now Playing // { "KEY_TITLE", 0x7689a25d, false }, // Now Playing // { "KEY_TEXT", 0x7689f807, false }, // Size // { "KEY_BRIGHTNESS_CYCLE", 0x768904fb, false }, { NULL, 0 , false }, }; static u32_t ir_cmd_map(const char *c) { int i; for (i = 0; cmdmap[i].cmd; i++) { if (!strcmp(c, cmdmap[i].cmd)) { return cmdmap[i].code; } } return 0; } static u32_t ir_key_map(const char *c, const char *r) { int i; for (i = 0; keymap[i].lirc; i++) { if (!strcmp(c, keymap[i].lirc)) { // inputlirc issues "0", while LIRC uses "00" if (keymap[i].repeat || !strcmp(r, "0") || !strcmp(r,"00")) { return keymap[i].code; } LOG_DEBUG("repeat suppressed"); break; } } return 0; } static void *ir_thread() { char *code; while (fd > 0 && LIRC(i, nextcode, &code) == 0) { u32_t now = gettime_ms(); u32_t ir_code = 0; if (code == NULL) continue; if (config) { // allow lirc_client to decode then lookup cmd in our table // we can only send one IR event to slimproto so break after first one char *c; while (LIRC(i, code2char, config, code, &c) == 0 && c != NULL) { ir_code = ir_cmd_map(c); if (ir_code) { LOG_DEBUG("ir cmd: %s -> %x", c, ir_code); } } } if (!ir_code) { // try to match on lirc button name if it is from the standard namespace // this allows use of non slim remotes without a specific entry in .lircrc char *b, *r; strtok(code, " \n"); // discard r = strtok(NULL, " \n"); // repeat count b = strtok(NULL, " \n"); // key name if (r && b) { ir_code = ir_key_map(b, r); LOG_DEBUG("ir lirc: %s [%s] -> %x", b, r, ir_code); } } if (ir_code) { LOCK_I; if (ir.code) { LOG_DEBUG("code dropped"); } ir.code = ir_code; ir.ts = now; UNLOCK_I; wake_controller(); } free(code); } return 0; } #if !LINKALL static bool load_lirc() { void *handle = dlopen(LIBLIRC, RTLD_NOW); char *err; if (!handle) { LOG_INFO("dlerror: %s", dlerror()); return false; } i->lirc_init = dlsym(handle, "lirc_init"); i->lirc_deinit = dlsym(handle, "lirc_deinit"); i->lirc_readconfig = dlsym(handle, "lirc_readconfig"); i->lirc_freeconfig = dlsym(handle, "lirc_freeconfig"); i->lirc_nextcode = dlsym(handle, "lirc_nextcode"); i->lirc_code2char = dlsym(handle, "lirc_code2char"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBLIRC); return true; } #endif void ir_init(log_level level, char *lircrc) { loglevel = level; #if !LINKALL i = malloc(sizeof(struct lirc)); if (!i || !load_lirc()) { return; } #endif fd = LIRC(i, init, LIRC_CLIENT_ID, 0); if (fd > 0) { if (LIRC(i, readconfig, lircrc, &config, NULL) != 0) { LOG_WARN("error reading config: %s", lircrc); } else { LOG_DEBUG("loaded lircrc config: %s", lircrc); } mutex_create(ir.mutex); pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + IR_THREAD_STACK_SIZE); pthread_create(&thread, &attr, ir_thread, NULL); pthread_attr_destroy(&attr); } else { LOG_WARN("failed to connect to lircd - ir processing disabled"); } } void ir_close(void) { if (fd > 0) { fd = -1; if (config) { LIRC(i, freeconfig, config); } LIRC(i, deinit); pthread_cancel(thread); pthread_join(thread, NULL); mutex_destroy(ir.mutex); } } #endif //#if IR squeezelite-1.9.9-1395+git20220104.874e4f9/mad.c000066400000000000000000000267611416534420200200150ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #include #define MAD_DELAY 529 #define READBUF_SIZE 2048 // local buffer used by decoder: FIXME merge with any other decoders needing one? struct mad { u8_t *readbuf; unsigned readbuf_len; struct mad_stream stream; struct mad_frame frame; struct mad_synth synth; enum mad_error last_error; // for lame gapless processing int checktags; u32_t consume; u32_t skip; u64_t samples; u32_t padding; #if !LINKALL // mad symbols to be dynamically loaded void (* mad_stream_init)(struct mad_stream *); void (* mad_frame_init)(struct mad_frame *); void (* mad_synth_init)(struct mad_synth *); void (* mad_frame_finish)(struct mad_frame *); void (* mad_stream_finish)(struct mad_stream *); void (* mad_stream_buffer)(struct mad_stream *, unsigned char const *, unsigned long); int (* mad_frame_decode)(struct mad_frame *, struct mad_stream *); void (* mad_synth_frame)(struct mad_synth *, struct mad_frame const *); char const *(* mad_stream_errorstr)(struct mad_stream const *); #endif }; static struct mad *m; extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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 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 IF_DIRECT(x) { x } #define IF_PROCESS(x) #endif #if LINKALL #define MAD(h, fn, ...) (mad_ ## fn)(__VA_ARGS__) #else #define MAD(h, fn, ...) (h)->mad_##fn(__VA_ARGS__) #endif // based on libmad minimad.c scale static inline ISAMPLE_T scale(mad_fixed_t sample) { sample += (1L << (MAD_F_FRACBITS - 24)); if (sample >= MAD_F_ONE) sample = MAD_F_ONE - 1; else if (sample < -MAD_F_ONE) sample = -MAD_F_ONE; #if BYTES_PER_FRAME == 4 return (ISAMPLE_T)((sample >> (MAD_F_FRACBITS + 1 - 24)) >> 8); #else return (ISAMPLE_T)((sample >> (MAD_F_FRACBITS + 1 - 24)) << 8); #endif } // check for id3.2 tag at start of file - http://id3.org/id3v2.4.0-structure, return length static unsigned _check_id3_tag(size_t bytes) { u8_t *ptr = streambuf->readp; u32_t size = 0; if (bytes > 10 && *ptr == 'I' && *(ptr+1) == 'D' && *(ptr+2) == '3') { // size is encoded as syncsafe integer, add 10 if footer present if (*(ptr+6) < 0x80 && *(ptr+7) < 0x80 && *(ptr+8) < 0x80 && *(ptr+9) < 0x80) { size = 10 + (*(ptr+6) << 21) + (*(ptr+7) << 14) + (*(ptr+8) << 7) + *(ptr+9) + ((*(ptr+5) & 0x10) ? 10 : 0); LOG_DEBUG("id3.2 tag len: %u", size); } } return size; } // check for lame gapless params, don't advance streambuf static void _check_lame_header(size_t bytes) { u8_t *ptr = streambuf->readp; if (*ptr == 0xff && (*(ptr+1) & 0xf0) == 0xf0 && bytes > 180) { u32_t frame_count = 0, enc_delay = 0, enc_padding = 0; u8_t flags; // 2 channels if (!memcmp(ptr + 36, "Xing", 4) || !memcmp(ptr + 36, "Info", 4)) { ptr += 36 + 7; // mono } else if (!memcmp(ptr + 21, "Xing", 4) || !memcmp(ptr + 21, "Info", 4)) { ptr += 21 + 7; } flags = *ptr; if (flags & 0x01) { frame_count = unpackN((u32_t *)(ptr + 1)); ptr += 4; } if (flags & 0x02) ptr += 4; if (flags & 0x04) ptr += 100; if (flags & 0x08) ptr += 4; if (!!memcmp(ptr+1, "LAME", 4)) { return; } ptr += 22; enc_delay = (*ptr << 4 | *(ptr + 1) >> 4) + MAD_DELAY; enc_padding = (*(ptr + 1) & 0xF) << 8 | *(ptr + 2); enc_padding = enc_padding > MAD_DELAY ? enc_padding - MAD_DELAY : 0; // add one frame to initial skip for this (empty) frame m->skip = enc_delay + 1152; m->samples = frame_count * 1152 - enc_delay - enc_padding; m->padding = enc_padding; LOG_INFO("gapless: skip: %u samples: " FMT_u64 " delay: %u padding: %u", m->skip, m->samples, enc_delay, enc_padding); } } static decode_state mad_decode(void) { size_t bytes; bool eos = false; LOCK_S; bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); if (m->checktags) { if (m->checktags == 1) { m->consume = _check_id3_tag(bytes); m->checktags = 2; } if (m->consume) { u32_t consume = min(m->consume, bytes); LOG_DEBUG("consume: %u of %u", consume, m->consume); _buf_inc_readp(streambuf, consume); m->consume -= consume; UNLOCK_S; return DECODE_RUNNING; } if (m->checktags == 2) { if (!stream.meta_interval) { _check_lame_header(bytes); } m->checktags = 0; } } if (m->stream.next_frame && m->readbuf_len) { m->readbuf_len -= m->stream.next_frame - m->readbuf; memmove(m->readbuf, m->stream.next_frame, m->readbuf_len); } bytes = min(bytes, READBUF_SIZE - m->readbuf_len); memcpy(m->readbuf + m->readbuf_len, streambuf->readp, bytes); m->readbuf_len += bytes; _buf_inc_readp(streambuf, bytes); if (stream.state <= DISCONNECT && _buf_used(streambuf) == 0) { eos = true; LOG_DEBUG("end of stream"); memset(m->readbuf + m->readbuf_len, 0, MAD_BUFFER_GUARD); m->readbuf_len += MAD_BUFFER_GUARD; } UNLOCK_S; MAD(m, stream_buffer, &m->stream, m->readbuf, m->readbuf_len); while (true) { size_t frames; s32_t *iptrl; s32_t *iptrr; unsigned max_frames; if (MAD(m, frame_decode, &m->frame, &m->stream) == -1) { decode_state ret; if (!eos && m->stream.error == MAD_ERROR_BUFLEN) { ret = DECODE_RUNNING; } else if (eos && (m->stream.error == MAD_ERROR_BUFLEN || m->stream.error == MAD_ERROR_LOSTSYNC || m->stream.error == MAD_ERROR_BADBITRATE)) { ret = DECODE_COMPLETE; } else if (!MAD_RECOVERABLE(m->stream.error)) { LOG_INFO("mad_frame_decode error: %s - stopping decoder", MAD(m, stream_errorstr, &m->stream)); ret = DECODE_COMPLETE; } else { if (m->stream.error != m->last_error) { // suppress repeat error messages LOG_DEBUG("mad_frame_decode error: %s", MAD(m, stream_errorstr, &m->stream)); } ret = DECODE_RUNNING; } m->last_error = m->stream.error; return ret; }; MAD(m, synth_frame, &m->synth, &m->frame); if (decode.new_stream) { LOCK_O; LOG_INFO("setting track_start"); output.next_sample_rate = decode_newstream(m->synth.pcm.samplerate, 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; } LOCK_O_direct; IF_DIRECT( max_frames = _buf_space(outputbuf) / BYTES_PER_FRAME; ); IF_PROCESS( max_frames = process.max_in_frames - process.in_frames; ); if (m->synth.pcm.length > max_frames) { LOG_WARN("too many samples - dropping samples"); m->synth.pcm.length = max_frames; } frames = m->synth.pcm.length; iptrl = m->synth.pcm.samples[0]; iptrr = m->synth.pcm.samples[ m->synth.pcm.channels - 1 ]; if (m->skip) { u32_t skip = min(m->skip, frames); LOG_DEBUG("gapless: skipping %u frames at start", skip); frames -= skip; m->skip -= skip; iptrl += skip; iptrr += skip; } if (m->samples) { if (m->samples < frames) { LOG_DEBUG("gapless: trimming %u frames from end", frames - m->samples); frames = (size_t)m->samples; } m->samples -= frames; if (m->samples > 0 && eos && !(m->stream.next_frame[0] == 0xff && (m->stream.next_frame[1] & 0xf0) == 0xf0)) { // this is the last frame to be decoded, but more samples expected so we must have skipped, remove padding // note this only works if the padding is less than one frame of 1152 bytes otherswise some gap will remain LOG_DEBUG("gapless: early end - trimming padding from end"); if (frames >= m->padding) { frames -= m->padding; } else { frames = 0; } m->samples = 0; } } LOG_SDEBUG("write %u frames", frames); while (frames > 0) { size_t f, count; ISAMPLE_T *optr; IF_DIRECT( f = min(frames, _buf_cont_write(outputbuf) / BYTES_PER_FRAME); optr = (ISAMPLE_T *)outputbuf->writep; ); IF_PROCESS( f = min(frames, process.max_in_frames - process.in_frames); optr = (ISAMPLE_T *)((u8_t *)process.inbuf + process.in_frames * BYTES_PER_FRAME); ); count = f; while (count--) { *optr++ = scale(*iptrl++); *optr++ = scale(*iptrr++); } frames -= f; IF_DIRECT( _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames += f; ); } UNLOCK_O_direct; } return eos ? DECODE_COMPLETE : DECODE_RUNNING; } static void mad_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { if (!m->readbuf) { m->readbuf = malloc(READBUF_SIZE + MAD_BUFFER_GUARD); } m->checktags = 1; m->consume = 0; m->skip = MAD_DELAY; m->samples = 0; m->readbuf_len = 0; m->last_error = MAD_ERROR_NONE; // explicitly clear before calling mad_*_init (again) to free the malloc'd memory MAD(m, stream_finish, &m->stream); MAD(m, frame_finish, &m->frame); mad_synth_finish(&m->synth); MAD(m, stream_init, &m->stream); MAD(m, frame_init, &m->frame); MAD(m, synth_init, &m->synth); } static void mad_close(void) { mad_synth_finish(&m->synth); // macro only in current version MAD(m, frame_finish, &m->frame); MAD(m, stream_finish, &m->stream); free(m->readbuf); m->readbuf = NULL; } static bool load_mad() { #if !LINKALL void *handle = dlopen(LIBMAD, RTLD_NOW); char *err; if (!handle) { LOG_INFO("dlerror: %s", dlerror()); return false; } m->mad_stream_init = dlsym(handle, "mad_stream_init"); m->mad_frame_init = dlsym(handle, "mad_frame_init"); m->mad_synth_init = dlsym(handle, "mad_synth_init"); m->mad_frame_finish = dlsym(handle, "mad_frame_finish"); m->mad_stream_finish = dlsym(handle, "mad_stream_finish"); m->mad_stream_buffer = dlsym(handle, "mad_stream_buffer"); m->mad_frame_decode = dlsym(handle, "mad_frame_decode"); m->mad_synth_frame = dlsym(handle, "mad_synth_frame"); m->mad_stream_errorstr = dlsym(handle, "mad_stream_errorstr"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBMAD); #endif return true; } struct codec *register_mad(void) { static struct codec ret = { 'm', // id "mp3", // types READBUF_SIZE, // min read 206800, // min space mad_open, // open mad_close, // close mad_decode, // decode }; m = calloc(1, sizeof(struct mad)); if (!m) { return NULL; } m->readbuf = NULL; m->readbuf_len = 0; if (!load_mad()) { return NULL; } LOG_INFO("using mad to decode mp3"); return &ret; } squeezelite-1.9.9-1395+git20220104.874e4f9/main.c000066400000000000000000000544261416534420200201770ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additions (c) Paul Hermann, 2015-2021 under the same license terms * -Control of Raspberry pi GPIO for amplifier power * -Launch script on power status change from LMS */ #include "squeezelite.h" #include #define TITLE "Squeezelite " VERSION ", Copyright 2012-2015 Adrian Smith, 2015-2021 Ralph Irving." #define CODECS_BASE "flac,pcm,mp3,ogg" #if NO_FAAD #define CODECS_AAC "" #else #define CODECS_AAC ",aac" #endif #if ALAC #define CODECS_FF ",alac" #elif FFMPEG #define CODECS_FF ",wma,alac" #else #define CODECS_FF "" #endif #if OPUS #define CODECS_OPUS ",ops" #else #define CODECS_OPUS "" #endif #if DSD #define CODECS_DSD ",dsd" #else #define CODECS_DSD "" #endif #define CODECS_MP3 " (mad,mpg for specific mp3 codec)" #define CODECS CODECS_BASE CODECS_AAC CODECS_FF CODECS_OPUS CODECS_DSD CODECS_MP3 static void usage(const char *argv0) { printf(TITLE " See -t for license terms\n" "Usage: %s [options]\n" " -s [:]\tConnect to specified server, otherwise uses autodiscovery to find server\n" " -o \tSpecify output device, default \"default\", - = output to stdout\n" " -l \t\t\tList output devices\n" #if ALSA " -a :

::\tSpecify ALSA params to open output device, b = buffer time in ms or size in bytes, p = period count or size in bytes, f sample format (16|24|24_3|32), m = use mmap (0|1)\n" #endif #if PORTAUDIO #if PA18API " -a :\tSpecify output target 4 byte frames per buffer, number of buffers\n" #elif OSX && !defined(OSXPPC) " -a :\t\tSpecify Portaudio params to open output device, l = target latency in ms, r = allow OSX to resample (0|1)\n" #elif WIN " -a :\t\tSpecify Portaudio params to open output device, l = target latency in ms, e = use exclusive mode for WASAPI (0|1)\n" #else " -a \t\tSpecify Portaudio params to open output device, l = target latency in ms\n" #endif #endif " -a \t\tSpecify sample format (16|24|32) of output file when using -o - to output samples to stdout (interleaved little endian only)\n" " -b :\tSpecify internal Stream and Output buffer sizes in Kbytes\n" " -c ,\tRestrict codecs to those specified, otherwise load all available codecs; known codecs: " CODECS "\n" " \t\t\tCodecs reported to LMS in order listed, allowing codec priority refinement.\n" " -C \t\tClose output device when idle after timeout seconds, default is to keep it open while player is 'on'\n" #if !IR " -d =\tSet logging level, logs: all|slimproto|stream|decode|output, level: info|debug|sdebug\n" #else " -d =\tSet logging level, logs: all|slimproto|stream|decode|output|ir, level: info|debug|sdebug\n" #endif #if defined(GPIO) && defined(RPI) " -G :\tSpecify the BCM GPIO# to use for Amp Power Relay and if the output should be Active High or Low\n" #endif " -e ,\tExplicitly exclude native support of one or more codecs; known codecs: " CODECS "\n" " -f \t\tWrite debug to logfile\n" #if IR " -i []\tEnable lirc remote control support (lirc config file ~/.lircrc used if filename not specified)\n" #endif " -m \t\tSet mac address, format: ab:cd:ef:12:34:56\n" " -M \tSet the squeezelite player model name sent to the server (default: " MODEL_NAME_STRING ")\n" " -n \t\tSet the player name\n" " -N \t\tStore player name in filename to allow server defined name changes to be shared between servers (not supported with -n)\n" " -W\t\t\tRead wave and aiff format from header, ignore server parameters\n" #if ALSA " -p \t\tSet real time priority of output thread (1-99)\n" #endif #if LINUX || FREEBSD || SUN " -P \t\tStore the process id (PID) in filename\n" #endif " -r [:]\tSample rates supported, allows output to be off when squeezelite is started; rates = |-|,,; delay = optional delay switching rates in ms\n" #if GPIO " -S \tAbsolute path to script to launch on power commands from LMS\n" #endif #if RESAMPLE " -R -u [params]\tResample, params = ::::::,\n" " \t\t\t recipe = (v|h|m|l|q)(L|I|M)(s) [E|X], E = exception - resample only if native rate not supported, X = async - resample to max rate for device, otherwise to max sync rate\n" " \t\t\t flags = num in hex,\n" " \t\t\t attenuation = attenuation in dB to apply (default is -1db if not explicitly set),\n" " \t\t\t precision = number of bits precision (NB. HQ = 20. VHQ = 28),\n" " \t\t\t passband_end = number in percent (0dB pt. bandwidth to preserve. nyquist = 100%%),\n" " \t\t\t stopband_start = number in percent (Aliasing/imaging control. > passband_end),\n" " \t\t\t phase_response = 0-100 (0 = minimum / 50 = linear / 100 = maximum)\n" #endif #if DSD #if ALSA " -D [delay][:format]\tOutput device supports DSD, delay = optional delay switching between PCM and DSD in ms\n" " \t\t\t format = dop (default if not specified), u8, u16le, u16be, u32le or u32be.\n" #else " -D [delay]\t\tOutput device supports DSD over PCM (DoP), delay = optional delay switching between PCM and DoP in ms\n" #endif #endif #if VISEXPORT " -v \t\t\tVisualizer support\n" #endif # if ALSA " -O \tSpecify mixer device, defaults to 'output device'\n" " -L \t\t\tList volume controls for output device\n" " -U \t\tUnmute ALSA control and set to full volume (not supported with -V)\n" " -V \t\tUse ALSA control for volume adjustment, otherwise use software volume adjustment\n" " -X \t\t\tUse linear volume adjustments instead of in terms of dB (only for hardware volume control)\n" #endif #if LINUX || FREEBSD || SUN " -z \t\t\tDaemonize\n" #endif " -Z \t\tReport rate to server in helo as the maximum sample rate we can support\n" " -t \t\t\tLicense terms\n" " -? \t\t\tDisplay this help text\n" "\n" "Build options:" #if SUN " SOLARIS" #elif LINUX " LINUX" #endif #if WIN " WIN" #endif #if OSX " OSX" #endif #if OSXPPC "PPC" #endif #if FREEBSD " FREEBSD" #endif #if ALSA " ALSA" #endif #if PORTAUDIO " PORTAUDIO" #if PA18API "18" #endif #endif #if PULSEAUDIO " PULSEAUDIO" #endif #if EVENTFD " EVENTFD" #endif #if SELFPIPE " SELFPIPE" #endif #if WINEVENT " WINEVENT" #endif #if RESAMPLE_MP " RESAMPLE_MP" #else #if RESAMPLE " RESAMPLE" #endif #endif #if ALAC " ALAC" #elif FFMPEG " FFMPEG" #endif #if OPUS " OPUS" #endif #if NO_FAAD " NO_FAAD" #endif #if VISEXPORT " VISEXPORT" #endif #if IR " IR" #endif #if GPIO " GPIO" #endif #if RPI " RPI" #endif #if DSD " DSD" #endif #if USE_SSL " SSL" #endif #if NO_SSLSYM " NO_SSLSYM" #endif #if LINKALL " LINKALL" #endif "\n\n", argv0); } static void license(void) { printf(TITLE "\n\n" "This program is free software: you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation, either version 3 of the License, or\n" "(at your option) any later version.\n\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n\n" "You should have received a copy of the GNU General Public License\n" "along with this program. If not, see .\n" "\nThe Squeezelite source code is available on github.\n" "\n" "\nThe source and patches for bundled 3rd party libraries can be found on\n" "SourceForge. \n" #if DSD "\nContains dsd2pcm library Copyright 2009, 2011 Sebastian Gesemann which\n" "is subject to its own license.\n" "\nContains the Daphile Project full dsd patch Copyright 2013-2017 Daphile,\n" "which is subject to its own license.\n" #endif "\nOption to allow server side upsampling for PCM streams (-W) from\n" "squeezelite-R2 (c) Marco Curti 2015, marcoc1712@gmail.com.\n" #if RPI "\nContains minimal GPIO Interface .\n" #endif #if FFMPEG "\nThis software uses libraries from the FFmpeg project under\n" "the LGPLv2.1 and its source can be downloaded from\n" "\n" #endif #if OPUS "\nOpus decoder support (c) Philippe 2018-2021, philippe_44@outlook.com\n" #endif #if ALAC "\nContains Apple Lossless (ALAC) decoder. Apache License Version 2.0\n" "Apple ALAC decoder support (c) Philippe 2018-2021, philippe_44@outlook.com\n" #endif "\n" ); } static void sighandler(int signum) { slimproto_stop(); // remove ourselves in case above does not work, second SIGINT will cause non gracefull shutdown signal(signum, SIG_DFL); } int main(int argc, char **argv) { char *server = NULL; char *output_device = "default"; char *include_codecs = NULL; char *exclude_codecs = ""; char *name = NULL; char *namefile = NULL; char *modelname = NULL; extern bool pcm_check_header; extern bool user_rates; char *logfile = NULL; u8_t mac[6]; unsigned stream_buf_size = STREAMBUF_SIZE; unsigned output_buf_size = 0; // set later unsigned rates[MAX_SUPPORTED_SAMPLERATES] = { 0 }; unsigned rate_delay = 0; char *resample = NULL; char *output_params = NULL; unsigned idle = 0; #if LINUX || FREEBSD || SUN bool daemonize = false; char *pidfile = NULL; FILE *pidfp = NULL; #endif #if ALSA unsigned rt_priority = OUTPUT_RT_PRIORITY; char *mixer_device = output_device; char *output_mixer = NULL; bool output_mixer_unmute = false; bool linear_volume = false; #endif #if DSD unsigned dsd_delay = 0; dsd_format dsd_outfmt = PCM; #endif #if VISEXPORT bool visexport = false; #endif #if IR char *lircrc = NULL; #endif log_level log_output = lWARN; log_level log_stream = lWARN; log_level log_decode = lWARN; log_level log_slimproto = lWARN; #if IR log_level log_ir = lWARN; #endif int maxSampleRate = 0; char *optarg = NULL; int optind = 1; int i; #define MAXCMDLINE 512 char cmdline[MAXCMDLINE] = ""; get_mac(mac); for (i = 0; i < argc && (strlen(argv[i]) + strlen(cmdline) + 2 < MAXCMDLINE); i++) { strcat(cmdline, argv[i]); strcat(cmdline, " "); } while (optind < argc && strlen(argv[optind]) >= 2 && argv[optind][0] == '-') { char *opt = argv[optind] + 1; if (strstr("oabcCdefmMnNpPrsZ" #if ALSA "UVO" #endif , opt) && optind < argc - 1) { optarg = argv[optind + 1]; optind += 2; } else if (strstr("ltz?W" #if ALSA "LX" #endif #if RESAMPLE "uR" #endif #if DSD "D" #endif #if VISEXPORT "v" #endif #if IR "i" #endif #if defined(GPIO) && defined(RPI) "G" #endif #if GPIO "S" #endif , opt)) { optarg = NULL; optind += 1; } else { fprintf(stderr, "\nOption error: -%s\n\n", opt); usage(argv[0]); exit(1); } switch (opt[0]) { case 'o': output_device = optarg; #if ALSA mixer_device = optarg; #endif break; case 'a': output_params = optarg; break; case 'b': { char *s = next_param(optarg, ':'); char *o = next_param(NULL, ':'); if (s) stream_buf_size = atoi(s) * 1024; if (o) output_buf_size = atoi(o) * 1024; } break; case 'c': include_codecs = optarg; break; case 'C': if (atoi(optarg) > 0) { idle = atoi(optarg) * 1000; } break; case 'e': exclude_codecs = optarg; break; case 'd': { char *l = strtok(optarg, "="); char *v = strtok(NULL, "="); log_level new = lWARN; if (l && v) { if (!strcmp(v, "info")) new = lINFO; if (!strcmp(v, "debug")) new = lDEBUG; if (!strcmp(v, "sdebug")) new = lSDEBUG; if (!strcmp(l, "all") || !strcmp(l, "slimproto")) log_slimproto = new; if (!strcmp(l, "all") || !strcmp(l, "stream")) log_stream = new; if (!strcmp(l, "all") || !strcmp(l, "decode")) log_decode = new; if (!strcmp(l, "all") || !strcmp(l, "output")) log_output = new; #if IR if (!strcmp(l, "all") || !strcmp(l, "ir")) log_ir = new; #endif } else { fprintf(stderr, "\nDebug settings error: -d %s\n\n", optarg); usage(argv[0]); exit(1); } } break; case 'f': logfile = optarg; break; case 'm': { int byte = 0; char *tmp; if (!strncmp(optarg, "00:04:20", 8)) { LOG_ERROR("ignoring mac address from hardware player range 00:04:20:**:**:**"); } else { char *t = strtok(optarg, ":"); while (t && byte < 6) { mac[byte++] = (u8_t)strtoul(t, &tmp, 16); t = strtok(NULL, ":"); } } } break; case 'M': modelname = optarg; break; case 'r': { char *rstr = next_param(optarg, ':'); char *dstr = next_param(NULL, ':'); if (rstr && strstr(rstr, ",")) { // parse sample rates and sort them char *r = next_param(rstr, ','); unsigned tmp[MAX_SUPPORTED_SAMPLERATES] = { 0 }; int i, j; int last = 999999; for (i = 0; r && i < MAX_SUPPORTED_SAMPLERATES; ++i) { tmp[i] = atoi(r); r = next_param(NULL, ','); } for (i = 0; i < MAX_SUPPORTED_SAMPLERATES; ++i) { int largest = 0; for (j = 0; j < MAX_SUPPORTED_SAMPLERATES; ++j) { if (tmp[j] > largest && tmp[j] < last) { largest = tmp[j]; } } rates[i] = last = largest; } } else if (rstr) { // optstr is - or , extract rates from test rates within this range unsigned ref[] TEST_RATES; char *str1 = next_param(rstr, '-'); char *str2 = next_param(NULL, '-'); unsigned max = str2 ? atoi(str2) : (str1 ? atoi(str1) : ref[0]); unsigned min = str1 && str2 ? atoi(str1) : 0; unsigned tmp; int i, j; if (max < min) { tmp = max; max = min; min = tmp; } rates[0] = max; for (i = 0, j = 1; i < MAX_SUPPORTED_SAMPLERATES; ++i) { if (ref[i] < rates[j-1] && ref[i] >= min) { rates[j++] = ref[i]; } } } if (dstr) { rate_delay = atoi(dstr); } if (rates[0]) { user_rates = true; } } break; case 's': server = optarg; break; case 'n': name = optarg; break; case 'N': namefile = optarg; break; case 'Z': maxSampleRate = atoi(optarg); break; case 'W': pcm_check_header = true; break; #if ALSA case 'p': rt_priority = atoi(optarg); if (rt_priority > 99 || rt_priority < 1) { fprintf(stderr, "\nError: invalid priority: %s\n\n", optarg); usage(argv[0]); exit(1); } break; #endif #if LINUX || FREEBSD || SUN case 'P': pidfile = optarg; break; #endif case 'l': list_devices(); exit(0); break; #if RESAMPLE case 'u': case 'R': if (optind < argc && argv[optind] && argv[optind][0] != '-') { resample = argv[optind++]; } else { resample = ""; } break; #endif #if DSD case 'D': dsd_outfmt = DOP; if (optind < argc && argv[optind] && argv[optind][0] != '-') { char *dstr = next_param(argv[optind++], ':'); char *fstr = next_param(NULL, ':'); dsd_delay = dstr ? atoi(dstr) : 0; if (fstr) { if (!strcmp(fstr, "dop")) dsd_outfmt = DOP; if (!strcmp(fstr, "u8")) dsd_outfmt = DSD_U8; if (!strcmp(fstr, "u16le")) dsd_outfmt = DSD_U16_LE; if (!strcmp(fstr, "u32le")) dsd_outfmt = DSD_U32_LE; if (!strcmp(fstr, "u16be")) dsd_outfmt = DSD_U16_BE; if (!strcmp(fstr, "u32be")) dsd_outfmt = DSD_U32_BE; if (!strcmp(fstr, "dop24")) dsd_outfmt = DOP_S24_LE; if (!strcmp(fstr, "dop24_3")) dsd_outfmt = DOP_S24_3LE; } } break; #endif #if VISEXPORT case 'v': visexport = true; break; #endif #if ALSA case 'O': mixer_device = optarg; break; case 'L': list_mixers(mixer_device); exit(0); break; case 'X': linear_volume = true; break; case 'U': output_mixer_unmute = true; case 'V': if (output_mixer) { fprintf(stderr, "-U and -V option should not be used at same time\n"); exit(1); } output_mixer = optarg; break; #endif #if IR case 'i': if (optind < argc && argv[optind] && argv[optind][0] != '-') { lircrc = argv[optind++]; } else { lircrc = "~/.lircrc"; // liblirc_client will expand ~/ } break; #endif #if defined(GPIO) && defined(RPI) case 'G': if (power_script != NULL){ fprintf(stderr, "-G and -S options cannot be used together \n\n" ); usage(argv[0]); exit(1); } if (optind < argc && argv[optind] && argv[optind][0] != '-') { char *gp = next_param(argv[optind++], ':'); char *go = next_param (NULL, ':'); gpio_pin = atoi(gp); if (go != NULL){ if ((strcmp(go, "H")==0)|(strcmp(go, "h")==0)){ gpio_active_low=false; }else if((strcmp(go, "L")==0)|(strcmp(go, "l")==0)){ gpio_active_low=true; }else{ fprintf(stderr,"Must set output to be active High or Low i.e. -G18:H or -G18:L\n"); usage(argv[0]); exit(1); } }else{ fprintf(stderr,"-G Option Error\n"); usage(argv[0]); exit(1); } gpio_active = true; relay(0); } else { fprintf(stderr, "Error in GPIO Pin assignment.\n"); usage(argv[0]); exit(1); } break; #endif #if GPIO case 'S': if (gpio_active){ fprintf(stderr, "-G and -S options cannot be used together \n\n" ); usage(argv[0]); exit(1); } if (optind < argc && argv[optind] && argv[optind][0] != '-') { power_script = argv[optind++]; if( access( power_script, R_OK|X_OK ) == -1 ) { // file doesn't exist fprintf(stderr, "Script %s, not found\n\n", argv[optind-1]); usage(argv[0]); exit(1); } } else { fprintf(stderr, "No Script Name Given.\n\n"); usage(argv[0]); exit(1); } relay_script(0); break; #endif #if LINUX || FREEBSD || SUN case 'z': daemonize = true; #if SUN init_daemonize(); #endif /* SUN */ break; #endif case 't': license(); exit(0); case '?': usage(argv[0]); exit(0); default: fprintf(stderr, "Arg error: %s\n", 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"); usage(argv[0]); exit(1); } signal(SIGINT, sighandler); signal(SIGTERM, sighandler); #if defined(SIGQUIT) signal(SIGQUIT, sighandler); #endif #if defined(SIGHUP) signal(SIGHUP, sighandler); #endif #if USE_SSL && !LINKALL && !NO_SSLSYM ssl_loaded = load_ssl_symbols(); #endif // set the output buffer size if not specified on the command line, take account of resampling if (!output_buf_size) { output_buf_size = OUTPUTBUF_SIZE; if (resample) { unsigned scale = 8; if (rates[0]) { scale = rates[0] / 44100; if (scale > 8) scale = 8; if (scale < 1) scale = 1; } output_buf_size *= scale; } } if (logfile) { if (!freopen(logfile, "a", stderr)) { fprintf(stderr, "error opening logfile %s: %s\n", logfile, strerror(errno)); } else { if (log_output >= lINFO || log_stream >= lINFO || log_decode >= lINFO || log_slimproto >= lINFO) { fprintf(stderr, "\n%s\n", cmdline); } } } #if LINUX || FREEBSD || SUN if (pidfile) { if (!(pidfp = fopen(pidfile, "w")) ) { fprintf(stderr, "Error opening pidfile %s: %s\n", pidfile, strerror(errno)); exit(1); } pidfile = realpath(pidfile, NULL); // daemonize will change cwd } if (daemonize) { if (daemon(0, logfile ? 1 : 0)) { fprintf(stderr, "error daemonizing: %s\n", strerror(errno)); } } if (pidfp) { fprintf(pidfp, "%d\n", (int) getpid()); fclose(pidfp); } #endif #if WIN winsock_init(); #endif stream_init(log_stream, stream_buf_size); if (!strcmp(output_device, "-")) { output_init_stdout(log_output, output_buf_size, output_params, rates, rate_delay); } else { #if ALSA output_init_alsa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, rt_priority, idle, mixer_device, output_mixer, output_mixer_unmute, linear_volume); #endif #if PORTAUDIO output_init_pa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle); #endif #if PULSEAUDIO output_init_pulse(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle); #endif } #if DSD dsd_init(dsd_outfmt, dsd_delay); #endif #if VISEXPORT if (visexport) { output_vis_init(log_output, mac); } #endif decode_init(log_decode, include_codecs, exclude_codecs); #if RESAMPLE if (resample) { process_init(resample); } #endif #if IR if (lircrc) { ir_init(log_ir, lircrc); } #endif if (name && namefile) { fprintf(stderr, "-n and -N option should not be used at same time\n"); exit(1); } slimproto(log_slimproto, server, mac, name, namefile, modelname, maxSampleRate); decode_close(); stream_close(); if (!strcmp(output_device, "-")) { output_close_stdout(); } else { #if ALSA output_close_alsa(); #endif #if PORTAUDIO output_close_pa(); #endif #if PULSEAUDIO output_close_pulse(); #endif } #if IR ir_close(); #endif #if WIN winsock_close(); #endif #if LINUX || FREEBSD || SUN if (pidfile) { unlink(pidfile); free(pidfile); } #endif #if USE_SSL && !LINKALL && !NO_SSLSYM free_ssl_symbols(); #endif exit(0); } squeezelite-1.9.9-1395+git20220104.874e4f9/minimal_gpio.c000066400000000000000000000144021416534420200217050ustar00rootroot00000000000000/* minimal_gpio.c 2019-07-03 Public Domain Original Site: http://abyz.me.uk/rpi/pigpio/examples.html */ #include #include #include #include #include #include #include #include static volatile uint32_t piPeriphBase = 0x20000000; static volatile int pi_is_2711 = 0; #define SYST_BASE (piPeriphBase + 0x003000) #define DMA_BASE (piPeriphBase + 0x007000) #define CLK_BASE (piPeriphBase + 0x101000) #define GPIO_BASE (piPeriphBase + 0x200000) #define UART0_BASE (piPeriphBase + 0x201000) #define PCM_BASE (piPeriphBase + 0x203000) #define SPI0_BASE (piPeriphBase + 0x204000) #define I2C0_BASE (piPeriphBase + 0x205000) #define PWM_BASE (piPeriphBase + 0x20C000) #define BSCS_BASE (piPeriphBase + 0x214000) #define UART1_BASE (piPeriphBase + 0x215000) #define I2C1_BASE (piPeriphBase + 0x804000) #define I2C2_BASE (piPeriphBase + 0x805000) #define DMA15_BASE (piPeriphBase + 0xE05000) #define DMA_LEN 0x1000 /* allow access to all channels */ #define CLK_LEN 0xA8 #define GPIO_LEN 0xF4 #define SYST_LEN 0x1C #define PCM_LEN 0x24 #define PWM_LEN 0x28 #define I2C_LEN 0x1C #define BSCS_LEN 0x40 #define GPSET0 7 #define GPSET1 8 #define GPCLR0 10 #define GPCLR1 11 #define GPLEV0 13 #define GPLEV1 14 #define GPPUD 37 #define GPPUDCLK0 38 #define GPPUDCLK1 39 /* BCM2711 has different pulls */ #define GPPUPPDN0 57 #define GPPUPPDN1 58 #define GPPUPPDN2 59 #define GPPUPPDN3 60 #define SYST_CS 0 #define SYST_CLO 1 #define SYST_CHI 2 static volatile uint32_t *gpioReg = MAP_FAILED; static volatile uint32_t *systReg = MAP_FAILED; static volatile uint32_t *bscsReg = MAP_FAILED; #define PI_BANK (gpio>>5) #define PI_BIT (1<<(gpio&0x1F)) /* gpio modes. */ #define PI_INPUT 0 #define PI_OUTPUT 1 #define PI_ALT0 4 #define PI_ALT1 5 #define PI_ALT2 6 #define PI_ALT3 7 #define PI_ALT4 3 #define PI_ALT5 2 void gpioSetMode(unsigned gpio, unsigned mode) { int reg, shift; reg = gpio/10; shift = (gpio%10) * 3; gpioReg[reg] = (gpioReg[reg] & ~(7<> shift) & 7; } /* Values for pull-ups/downs off, pull-down and pull-up. */ #define PI_PUD_OFF 0 #define PI_PUD_DOWN 1 #define PI_PUD_UP 2 void gpioSetPullUpDown(unsigned gpio, unsigned pud) { int shift = (gpio & 0xf) << 1; uint32_t bits; uint32_t pull = 0; if (pi_is_2711) { switch (pud) { case PI_PUD_OFF: pull = 0; break; case PI_PUD_UP: pull = 1; break; case PI_PUD_DOWN: pull = 2; break; } bits = *(gpioReg + GPPUPPDN0 + (gpio>>4)); bits &= ~(3 << shift); bits |= (pull << shift); *(gpioReg + GPPUPPDN0 + (gpio>>4)) = bits; } else { *(gpioReg + GPPUD) = pud; usleep(20); *(gpioReg + GPPUDCLK0 + PI_BANK) = PI_BIT; usleep(20); *(gpioReg + GPPUD) = 0; *(gpioReg + GPPUDCLK0 + PI_BANK) = 0; } } int gpioRead(unsigned gpio) { if ((*(gpioReg + GPLEV0 + PI_BANK) & PI_BIT) != 0) return 1; else return 0; } void gpioWrite(unsigned gpio, unsigned level) { if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; } void gpioTrigger(unsigned gpio, unsigned pulseLen, unsigned level) { if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; usleep(pulseLen); if (level != 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; } /* Bit (1<= 8) { piPeriphBase = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7]; if (!piPeriphBase) piPeriphBase = buf[8]<<24 | buf[9]<<16 | buf[10]<<8 | buf[11]; if (piPeriphBase == 0xFE000000) pi_is_2711 = 1; } fclose(filp); } return rev; } /* Returns the number of microseconds after system boot. Wraps around after 1 hour 11 minutes 35 seconds. */ uint32_t gpioTick(void) { return systReg[SYST_CLO]; } /* Map in registers. */ static uint32_t * initMapMem(int fd, uint32_t addr, uint32_t len) { return (uint32_t *) mmap(0, len, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED|MAP_LOCKED, fd, addr); } int gpioInitialise(void) { int fd; gpioHardwareRevision(); /* sets rev and peripherals base address */ fd = open("/dev/mem", O_RDWR | O_SYNC) ; if (fd < 0) { fprintf(stderr, "This program needs root privileges. Try using sudo\n"); return -1; } gpioReg = initMapMem(fd, GPIO_BASE, GPIO_LEN); systReg = initMapMem(fd, SYST_BASE, SYST_LEN); bscsReg = initMapMem(fd, BSCS_BASE, BSCS_LEN); close(fd); if ((gpioReg == MAP_FAILED) || (systReg == MAP_FAILED) || (bscsReg == MAP_FAILED)) { fprintf(stderr, "Bad, mmap failed\n"); return -1; } return 0; } squeezelite-1.9.9-1395+git20220104.874e4f9/mpg.c000066400000000000000000000157321416534420200200330ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #include #define READ_SIZE 512 #define WRITE_SIZE 32 * 1024 struct mpg { mpg123_handle *h; bool use16bit; #if !LINKALL // mpg symbols to be dynamically loaded int (* mpg123_init)(void); int (* mpg123_feature)(const enum mpg123_feature_set); void (* mpg123_rates)(const long **, size_t *); int (* mpg123_format_none)(mpg123_handle *); int (* mpg123_format)(mpg123_handle *, long, int, int); mpg123_handle *(* mpg123_new)(const char*, int *); void (* mpg123_delete)(mpg123_handle *); int (* mpg123_open_feed)(mpg123_handle *); int (* mpg123_decode)(mpg123_handle *, const unsigned char *, size_t, unsigned char *, size_t, size_t *); int (* mpg123_getformat)(mpg123_handle *, long *, int *, int *); const char* (* mpg123_plain_strerror)(int); #endif }; static struct mpg *m; extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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 MPG123(h, fn, ...) (mpg123_ ## fn)(__VA_ARGS__) #else #define MPG123(h, fn, ...) (h)->mpg123_##fn(__VA_ARGS__) #endif static decode_state mpg_decode(void) { size_t bytes, space, size; int ret; u8_t *write_buf; LOCK_S; LOCK_O_direct; bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); IF_DIRECT( space = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)); write_buf = outputbuf->writep; ); IF_PROCESS( space = process.max_in_frames; write_buf = process.inbuf; ); bytes = min(bytes, READ_SIZE); space = min(space, WRITE_SIZE); if (m->use16bit) { space = (space / BYTES_PER_FRAME) * 4; } // only get the new stream information on first call so we can reset decode.direct appropriately if (decode.new_stream) { space = 0; } ret = MPG123(m, decode, m->h, streambuf->readp, bytes, write_buf, space, &size); if (ret == MPG123_NEW_FORMAT) { if (decode.new_stream) { long rate; int channels, enc; MPG123(m, getformat, m->h, &rate, &channels, &enc); LOG_INFO("setting track_start"); LOCK_O_not_direct; output.next_sample_rate = decode_newstream(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_not_direct; } else { LOG_WARN("format change mid stream - not supported"); } } // expand 16bit output to 32bit samples if (m->use16bit) { s16_t *iptr; s32_t *optr; size_t count = size / 2; size = count * 4; iptr = (s16_t *)write_buf + count; optr = (s32_t *)write_buf + count; while (count--) { *--optr = *--iptr << 16; } } _buf_inc_readp(streambuf, bytes); IF_DIRECT( _buf_inc_writep(outputbuf, size); ); IF_PROCESS( process.in_frames = size / BYTES_PER_FRAME; ); UNLOCK_O_direct; LOG_SDEBUG("write %u frames", size / BYTES_PER_FRAME); if (ret == MPG123_DONE || (bytes == 0 && size == 0 && stream.state <= DISCONNECT)) { UNLOCK_S; LOG_INFO("stream complete"); return DECODE_COMPLETE; } UNLOCK_S; if (ret == MPG123_ERR) { LOG_WARN("Error"); return DECODE_COMPLETE; } // OK and NEED_MORE keep running return DECODE_RUNNING; } static void mpg_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { int err; const long *list; size_t count, i; if (m->h) { MPG123(m, delete, m->h); } m->h = MPG123(m, new, NULL, &err); if (m->h == NULL) { LOG_WARN("new error: %s", MPG123(m, plain_strerror, err)); } // restrict output to 32bit or 16bit signed 2 channel based on library capability MPG123(m, rates, &list, &count); MPG123(m, format_none, m->h); for (i = 0; i < count; i++) { MPG123(m, format, m->h, list[i], 2, m->use16bit ? MPG123_ENC_SIGNED_16 : MPG123_ENC_SIGNED_32); } err = MPG123(m, open_feed, m->h); if (err) { LOG_WARN("open feed error: %s", MPG123(m, plain_strerror, err)); } } static void mpg_close(void) { MPG123(m, delete, m->h); m->h = NULL; } static bool load_mpg() { #if !LINKALL void *handle = dlopen(LIBMPG, RTLD_NOW); char *err; if (!handle) { LOG_INFO("dlerror: %s", dlerror()); return false; } m->mpg123_init = dlsym(handle, "mpg123_init"); m->mpg123_feature = dlsym(handle, "mpg123_feature"); m->mpg123_rates = dlsym(handle, "mpg123_rates"); m->mpg123_format_none = dlsym(handle, "mpg123_format_none"); m->mpg123_format = dlsym(handle, "mpg123_format"); m->mpg123_new = dlsym(handle, "mpg123_new"); m->mpg123_delete = dlsym(handle, "mpg123_delete"); m->mpg123_open_feed = dlsym(handle, "mpg123_open_feed"); m->mpg123_decode = dlsym(handle, "mpg123_decode"); m->mpg123_getformat = dlsym(handle, "mpg123_getformat"); m->mpg123_plain_strerror = dlsym(handle, "mpg123_plain_strerror"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBMPG); #endif return true; } struct codec *register_mpg(void) { static struct codec ret = { 'm', // id "mp3", // types READ_SIZE, // min read WRITE_SIZE, // min space mpg_open, // open mpg_close, // close mpg_decode, // decode }; m = malloc(sizeof(struct mpg)); if (!m) { return NULL; } m->h = NULL; if (!load_mpg()) { return NULL; } MPG123(m, init); m->use16bit = MPG123(m, feature, MPG123_FEATURE_OUTPUT_32BIT); LOG_INFO("using mpg to decode mp3"); return &ret; } squeezelite-1.9.9-1395+git20220104.874e4f9/opus.c000066400000000000000000000175771416534420200202470ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * Philippe 2018-2021, philippe_44@outlook.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #if OPUS /* * 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 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) #else #define ALIGN(n) (n << 16) #endif #include struct opus { struct OggOpusFile *of; #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 }; static struct opus *u; extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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__) #else #define OP(h, fn, ...) (h)->op_ ## 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; LOCK_S; bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); bytes = min(bytes, size); memcpy(ptr, streambuf->readp, bytes); _buf_inc_readp(streambuf, bytes); UNLOCK_S; return bytes; } static decode_state opus_decompress(void) { frames_t frames; int n; static int channels; u8_t *write_buf; LOCK_S; if (stream.state <= DISCONNECT && !_buf_used(streambuf)) { 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; } 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; ) output.track_start = outputbuf->writep; if (output.fade_mode) _checkfade(true); decode.new_stream = false; UNLOCK_O; channels = info->channel_count; 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; ); // 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); #if FRAME_BUF LOCK_O_direct; #endif if (n > 0) { frames_t count; s16_t *iptr; ISAMPLE_T *optr; frames = n; 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 #else while (count--) { *--optr = ALIGN(*--iptr); } #endif } else if (channels == 1) { while (count--) { *--optr = ALIGN(*--iptr); *--optr = ALIGN(*iptr); } } IF_DIRECT( _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames = frames; ); LOG_SDEBUG("wrote %u frames", frames); } else if (n == 0) { if (stream.state <= DISCONNECT) { LOG_INFO("partial 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); 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; } } 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 bool load_opus(void) { #if !LINKALL void *handle = dlopen(LIBOPUS, RTLD_NOW); char *err; if (!handle) { LOG_INFO("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"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBOPUS); #endif return true; } struct codec *register_opus(void) { static struct codec ret = { 'u', // id "ops", // types 4*1024, // min read 32*1024, // min space opus_open, // open opus_close, // close opus_decompress, // decode }; u = malloc(sizeof(struct opus)); if (!u) { return NULL; } u->of = NULL; #if FRAME_BUF u->write_buf = NULL; #endif if (!load_opus()) { return NULL; } LOG_INFO("using opus to decode ops"); return &ret; } #endif /* OPUS */ squeezelite-1.9.9-1395+git20220104.874e4f9/output.c000066400000000000000000000347371416534420200206160ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // Common output function #include "squeezelite.h" static log_level loglevel; struct outputstate output; static struct buffer buf; struct buffer *outputbuf = &buf; u8_t *silencebuf; #if DSD u8_t *silencebuf_dsd; #endif bool user_rates = false; #define LOCK mutex_lock(outputbuf->mutex) #define UNLOCK mutex_unlock(outputbuf->mutex) // functions starting _* are called with mutex locked frames_t _output_frames(frames_t avail) { frames_t frames, size; bool silence; u8_t flags = output.channels; s32_t cross_gain_in = 0, cross_gain_out = 0; s32_t *cross_ptr = NULL; s32_t gainL = output.current_replay_gain ? gain(output.gainL, output.current_replay_gain) : output.gainL; s32_t gainR = output.current_replay_gain ? gain(output.gainR, output.current_replay_gain) : output.gainR; if (output.invert) { gainL = -gainL; gainR = -gainR; } frames = _buf_used(outputbuf) / BYTES_PER_FRAME; silence = false; // start when threshold met if (output.state == OUTPUT_BUFFER && (frames * BYTES_PER_FRAME) > output.threshold * output.next_sample_rate / 10 && frames > output.start_frames) { output.state = OUTPUT_RUNNING; LOG_INFO("start buffer frames: %u", frames); wake_controller(); } // skip ahead - consume outputbuf but play nothing if (output.state == OUTPUT_SKIP_FRAMES) { if (frames > 0) { frames_t skip = min(frames, output.skip_frames); LOG_INFO("skip %u of %u frames", skip, output.skip_frames); frames -= skip; output.frames_played += skip; while (skip > 0) { frames_t cont_frames = min(skip, _buf_cont_read(outputbuf) / BYTES_PER_FRAME); skip -= cont_frames; _buf_inc_readp(outputbuf, cont_frames * BYTES_PER_FRAME); } } output.state = OUTPUT_RUNNING; } // pause frames - play silence for required frames if (output.state == OUTPUT_PAUSE_FRAMES) { LOG_INFO("pause %u frames", output.pause_frames); if (output.pause_frames == 0) { output.state = OUTPUT_RUNNING; } else { silence = true; frames = min(avail, output.pause_frames); frames = min(frames, MAX_SILENCE_FRAMES); output.pause_frames -= frames; } } // start at - play silence until jiffies reached if (output.state == OUTPUT_START_AT) { u32_t now = gettime_ms(); if (now >= output.start_at || output.start_at > now + 10000) { output.state = OUTPUT_RUNNING; } else { u32_t delta_frames = (output.start_at - now) * output.current_sample_rate / 1000; silence = true; frames = min(avail, delta_frames); frames = min(frames, MAX_SILENCE_FRAMES); } } // play silence if buffering or no frames if (output.state <= OUTPUT_BUFFER || frames == 0) { silence = true; frames = min(avail, MAX_SILENCE_FRAMES); } LOG_SDEBUG("avail: %d frames: %d silence: %d", avail, frames, silence); frames = min(frames, avail); size = frames; while (size > 0) { frames_t out_frames; frames_t cont_frames = _buf_cont_read(outputbuf) / BYTES_PER_FRAME; int wrote; if (output.track_start && !silence) { if (output.track_start == outputbuf->readp) { unsigned delay = 0; if (output.current_sample_rate != output.next_sample_rate) { delay = output.rate_delay; #if PULSEAUDIO set_sample_rate(output.next_sample_rate); #endif } IF_DSD( if (output.outfmt != output.next_fmt) { delay = output.dsd_delay; } ) frames -= size; // add silence delay in two halves, before and after track start on rate or pcm-dop change if (delay) { output.state = OUTPUT_PAUSE_FRAMES; if (!output.delay_active) { output.pause_frames = output.current_sample_rate * delay / 2000; output.delay_active = true; // first delay - don't process track start break; } else { output.pause_frames = output.next_sample_rate * delay / 2000; output.delay_active = false; // second delay - process track start } } LOG_INFO("track start sample rate: %u replay_gain: %u", output.next_sample_rate, output.next_replay_gain); output.frames_played = 0; output.track_started = true; output.track_start_time = gettime_ms(); output.current_sample_rate = output.next_sample_rate; IF_DSD( output.outfmt = output.next_fmt; ) if (output.fade == FADE_INACTIVE || output.fade_mode != FADE_CROSSFADE) { output.current_replay_gain = output.next_replay_gain; } output.track_start = NULL; break; } else if (output.track_start > outputbuf->readp) { // reduce cont_frames so we find the next track start at beginning of next chunk cont_frames = min(cont_frames, (output.track_start - outputbuf->readp) / BYTES_PER_FRAME); } } IF_DSD( if (output.outfmt != PCM) { gainL = gainR = FIXED_ONE; } ) if (output.fade && !silence) { if (output.fade == FADE_DUE) { if (output.fade_start == outputbuf->readp) { LOG_INFO("fade start reached"); output.fade = FADE_ACTIVE; } else if (output.fade_start > outputbuf->readp) { cont_frames = min(cont_frames, (output.fade_start - outputbuf->readp) / BYTES_PER_FRAME); } } if (output.fade == FADE_ACTIVE) { // find position within fade frames_t cur_f = outputbuf->readp >= output.fade_start ? (outputbuf->readp - output.fade_start) / BYTES_PER_FRAME : (outputbuf->readp + outputbuf->size - output.fade_start) / BYTES_PER_FRAME; frames_t dur_f = output.fade_end >= output.fade_start ? (output.fade_end - output.fade_start) / BYTES_PER_FRAME : (output.fade_end + outputbuf->size - output.fade_start) / BYTES_PER_FRAME; if (cur_f >= dur_f) { if (output.fade_mode == FADE_INOUT && output.fade_dir == FADE_DOWN) { LOG_INFO("fade down complete, starting fade up"); output.fade_dir = FADE_UP; output.fade_start = outputbuf->readp; output.fade_end = outputbuf->readp + dur_f * BYTES_PER_FRAME; if (output.fade_end >= outputbuf->wrap) { output.fade_end -= outputbuf->size; } cur_f = 0; } else if (output.fade_mode == FADE_CROSSFADE) { LOG_INFO("crossfade complete"); if (_buf_used(outputbuf) >= dur_f * BYTES_PER_FRAME) { _buf_inc_readp(outputbuf, dur_f * BYTES_PER_FRAME); LOG_INFO("skipped crossfaded start"); } else { LOG_WARN("unable to skip crossfaded start"); } output.fade = FADE_INACTIVE; output.current_replay_gain = output.next_replay_gain; } else { LOG_INFO("fade complete"); output.fade = FADE_INACTIVE; } } // if fade in progress set fade gain, ensure cont_frames reduced so we get to end of fade at start of chunk if (output.fade) { if (output.fade_end > outputbuf->readp) { cont_frames = min(cont_frames, (output.fade_end - outputbuf->readp) / BYTES_PER_FRAME); } if (output.fade_dir == FADE_UP || output.fade_dir == FADE_DOWN) { // fade in, in-out, out handled via altering standard gain s32_t fade_gain; if (output.fade_dir == FADE_DOWN) { cur_f = dur_f - cur_f; } fade_gain = to_gain((float)cur_f / (float)dur_f); gainL = gain(gainL, fade_gain); gainR = gain(gainR, fade_gain); if (output.invert) { gainL = -gainL; gainR = -gainR; } } if (output.fade_dir == FADE_CROSS) { // cross fade requires special treatment - performed later based on these values // support different replay gain for old and new track by retaining old value until crossfade completes if (_buf_used(outputbuf) / BYTES_PER_FRAME > dur_f + size) { cross_gain_in = to_gain((float)cur_f / (float)dur_f); cross_gain_out = FIXED_ONE - cross_gain_in; if (output.current_replay_gain) { cross_gain_out = gain(cross_gain_out, output.current_replay_gain); } if (output.next_replay_gain) { cross_gain_in = gain(cross_gain_in, output.next_replay_gain); } gainL = output.gainL; gainR = output.gainR; if (output.invert) { gainL = -gainL; gainR = -gainR; } cross_ptr = (s32_t *)(output.fade_end + cur_f * BYTES_PER_FRAME); } else { LOG_INFO("unable to continue crossfade - too few samples"); output.fade = FADE_INACTIVE; } } } } } out_frames = !silence ? min(size, cont_frames) : size; IF_DSD( if (output.outfmt != PCM) { flags = 0; } ) wrote = output.write_cb(out_frames, silence, gainL, gainR, flags, cross_gain_in, cross_gain_out, &cross_ptr); if (wrote <= 0) { frames -= size; break; } else { out_frames = (frames_t)wrote; } size -= out_frames; _vis_export(outputbuf, &output, out_frames, silence); if (!silence) { _buf_inc_readp(outputbuf, out_frames * BYTES_PER_FRAME); output.frames_played += out_frames; } } LOG_SDEBUG("wrote %u frames", frames); return frames; } void _checkfade(bool start) { frames_t bytes; LOG_INFO("fade mode: %u duration: %u %s", output.fade_mode, output.fade_secs, start ? "track-start" : "track-end"); bytes = output.next_sample_rate * BYTES_PER_FRAME * output.fade_secs; if (output.fade_mode == FADE_INOUT) { /* align on a frame boundary */ bytes = ((bytes / 2) / BYTES_PER_FRAME) * BYTES_PER_FRAME; } if (start && (output.fade_mode == FADE_IN || (output.fade_mode == FADE_INOUT && _buf_used(outputbuf) == 0))) { bytes = min(bytes, outputbuf->size - BYTES_PER_FRAME); // shorter than full buffer otherwise start and end align LOG_INFO("fade IN: %u frames", bytes / BYTES_PER_FRAME); output.fade = FADE_DUE; output.fade_dir = FADE_UP; output.fade_start = outputbuf->writep; output.fade_end = output.fade_start + bytes; if (output.fade_end >= outputbuf->wrap) { output.fade_end -= outputbuf->size; } } if (!start && (output.fade_mode == FADE_OUT || output.fade_mode == FADE_INOUT)) { bytes = min(_buf_used(outputbuf), bytes); LOG_INFO("fade %s: %u frames", output.fade_mode == FADE_INOUT ? "IN-OUT" : "OUT", bytes / BYTES_PER_FRAME); output.fade = FADE_DUE; output.fade_dir = FADE_DOWN; output.fade_start = outputbuf->writep - bytes; if (output.fade_start < outputbuf->buf) { output.fade_start += outputbuf->size; } output.fade_end = outputbuf->writep; } if (start && output.fade_mode == FADE_CROSSFADE) { if (_buf_used(outputbuf) != 0) { if (output.next_sample_rate != output.current_sample_rate) { LOG_INFO("crossfade disabled as sample rates differ"); return; } bytes = min(bytes, _buf_used(outputbuf)); // max of current remaining samples from previous track bytes = min(bytes, (frames_t)(outputbuf->size * 0.9)); // max of 90% of outputbuf as we consume additional buffer during crossfade LOG_INFO("CROSSFADE: %u frames", bytes / BYTES_PER_FRAME); output.fade = FADE_DUE; output.fade_dir = FADE_CROSS; output.fade_start = outputbuf->writep - bytes; if (output.fade_start < outputbuf->buf) { output.fade_start += outputbuf->size; } output.fade_end = outputbuf->writep; output.track_start = output.fade_start; } else if (outputbuf->size == OUTPUTBUF_SIZE && outputbuf->readp == outputbuf->buf) { // if default setting used and nothing in buffer attempt to resize to provide full crossfade support LOG_INFO("resize outputbuf for crossfade"); _buf_resize(outputbuf, OUTPUTBUF_SIZE_CROSSFADE); #if LINUX || FREEBSD touch_memory(outputbuf->buf, outputbuf->size); #endif } } } void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[], unsigned idle) { unsigned i; loglevel = level; output_buf_size = output_buf_size - (output_buf_size % BYTES_PER_FRAME); LOG_DEBUG("outputbuf size: %u", output_buf_size); buf_init(outputbuf, output_buf_size); if (!outputbuf->buf) { LOG_ERROR("unable to malloc output buffer"); exit(0); } silencebuf = malloc(MAX_SILENCE_FRAMES * BYTES_PER_FRAME); if (!silencebuf) { LOG_ERROR("unable to malloc silence buffer"); exit(0); } memset(silencebuf, 0, MAX_SILENCE_FRAMES * BYTES_PER_FRAME); IF_DSD( silencebuf_dsd = malloc(MAX_SILENCE_FRAMES * BYTES_PER_FRAME); if (!silencebuf_dsd) { LOG_ERROR("unable to malloc silence dsd buffer"); exit(0); } dsd_silence_frames((u32_t *)silencebuf_dsd, MAX_SILENCE_FRAMES); ) LOG_DEBUG("idle timeout: %u", idle); output.state = idle ? OUTPUT_OFF: OUTPUT_STOPPED; output.device = device; output.fade = FADE_INACTIVE; output.invert = false; output.error_opening = false; output.idle_to = (u32_t) idle; /* Skip test_open for stdout, set default sample rates */ if ( output.device[0] == '-' || user_rates ) { for (i = 0; i < MAX_SUPPORTED_SAMPLERATES; ++i) { output.supported_rates[i] = rates[i]; } } else { if (!test_open(output.device, output.supported_rates, user_rates)) { LOG_ERROR("unable to open output device: %s", output.device); exit(0); } } // set initial sample rate, preferring 44100 for (i = 0; i < MAX_SUPPORTED_SAMPLERATES; ++i) { if (output.supported_rates[i] == 44100) { output.default_sample_rate = 44100; break; } } if (!output.default_sample_rate) { output.default_sample_rate = output.supported_rates[0]; } output.current_sample_rate = output.default_sample_rate; if (loglevel >= lINFO) { char rates_buf[10 * MAX_SUPPORTED_SAMPLERATES] = ""; for (i = 0; output.supported_rates[i]; ++i) { char s[10]; sprintf(s, "%d ", output.supported_rates[i]); strcat(rates_buf, s); } LOG_INFO("supported rates: %s", rates_buf); } } void output_close_common(void) { buf_destroy(outputbuf); free(silencebuf); IF_DSD( free(silencebuf_dsd); ) } void output_flush(void) { LOG_INFO("flush output buffer"); buf_flush(outputbuf); LOCK; output.fade = FADE_INACTIVE; if (output.state != OUTPUT_OFF) { output.state = OUTPUT_STOPPED; output.stop_time = gettime_ms(); if (output.error_opening) { output.current_sample_rate = output.default_sample_rate; } output.delay_active = false; } output.frames_played = 0; UNLOCK; } squeezelite-1.9.9-1395+git20220104.874e4f9/output_alsa.c000066400000000000000000000716541416534420200216150ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additions (c) Paul Hermann, 2015-2021 under the same license terms * -Control of Raspberry pi GPIO for amplifier power * -Launch script on power status change from LMS */ // Output using Alsa #include "squeezelite.h" #if ALSA #include #include #include #include #define MAX_DEVICE_LEN 128 static snd_pcm_format_t fmts[] = { SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_UNKNOWN }; #if SL_LITTLE_ENDIAN #define NATIVE_FORMAT SND_PCM_FORMAT_S32_LE #else #define NATIVE_FORMAT SND_PCM_FORMAT_S32_BE #endif // ouput device static struct { char device[MAX_DEVICE_LEN + 1]; char *ctl; char *mixer_ctl; snd_pcm_format_t format; #if DSD dsd_format outfmt; snd_pcm_format_t pcmfmt; #endif snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; unsigned rate; bool mmap; bool reopen; u8_t *write_buf; const char *volume_mixer_name; bool mixer_linear; snd_mixer_elem_t* mixer_elem; snd_mixer_t *mixer_handle; long mixer_min; long mixer_max; } alsa; static snd_pcm_t *pcmp = NULL; extern u8_t *silencebuf; #if DSD extern u8_t *silencebuf_dsd; #endif static log_level loglevel; static bool running = true; extern struct outputstate output; extern struct buffer *outputbuf; #define LOCK mutex_lock(outputbuf->mutex) #define UNLOCK mutex_unlock(outputbuf->mutex) static char *ctl4device(const char *device) { char *ctl = NULL; if (!strncmp(device, "hw:", 3)) ctl = strdup(device); else if (!strncmp(device, "plughw:", 7)) ctl = strdup(device + 4); if (ctl) { char *comma; if ((comma = strrchr(ctl, ','))) *comma = '\0'; } else ctl = strdup(device); return ctl; } void list_devices(void) { void **hints, **n; if (snd_device_name_hint(-1, "pcm", &hints) >= 0) { n = hints; printf("Output devices:\n"); while (*n) { char *name = snd_device_name_get_hint(*n, "NAME"); char *desc = snd_device_name_get_hint(*n, "DESC"); if (name) printf(" %-30s", name); if (desc) { char *s1 = strtok(desc, "\n"); char *s2 = strtok(NULL, "\n"); if (s1) printf(" - %s", s1); if (s2) printf(" - %s", s2); } printf("\n"); if (name) free(name); if (desc) free(desc); n++; } snd_device_name_free_hint(hints); } printf("\n"); } void list_mixers(const char *output_device) { int err; snd_mixer_t *handle; snd_mixer_selem_id_t *sid; snd_mixer_elem_t *elem; char *ctl = ctl4device(output_device); snd_mixer_selem_id_alloca(&sid); LOG_INFO("listing mixers for: %s", output_device); if ((err = snd_mixer_open(&handle, 0)) < 0) { LOG_ERROR("open error: %s", snd_strerror(err)); return; } if ((err = snd_mixer_attach(handle, ctl)) < 0) { LOG_ERROR("attach error: %s", snd_strerror(err)); snd_mixer_close(handle); free(ctl); return; } free(ctl); if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { LOG_ERROR("register error: %s", snd_strerror(err)); snd_mixer_close(handle); return; } if ((err = snd_mixer_load(handle)) < 0) { LOG_ERROR("load error: %s", snd_strerror(err)); snd_mixer_close(handle); return; } printf("Volume controls for %s\n", output_device); for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem)) { if (snd_mixer_selem_has_playback_volume(elem)) { snd_mixer_selem_get_id(elem, sid); printf(" %s", snd_mixer_selem_id_get_name(sid)); if (snd_mixer_selem_id_get_index(sid)) { printf(",%d", snd_mixer_selem_id_get_index(sid)); } printf("\n"); } } printf("\n"); snd_mixer_close(handle); } #define MINVOL_DB 72 // LMS volume map for SqueezePlay sends values in range ~ -72..0 dB static void set_mixer(bool setmax, float ldB, float rdB) { int err; long nleft, nright; if (alsa.mixer_linear) { long lraw, rraw; if (setmax) { lraw = rraw = alsa.mixer_max; } else { lraw = ((ldB > -MINVOL_DB ? MINVOL_DB + floor(ldB) : 0) / MINVOL_DB * (alsa.mixer_max-alsa.mixer_min)) + alsa.mixer_min; rraw = ((rdB > -MINVOL_DB ? MINVOL_DB + floor(rdB) : 0) / MINVOL_DB * (alsa.mixer_max-alsa.mixer_min)) + alsa.mixer_min; } LOG_DEBUG("setting vol raw [%ld..%ld]", alsa.mixer_min, alsa.mixer_max); if ((err = snd_mixer_selem_set_playback_volume(alsa.mixer_elem, SND_MIXER_SCHN_FRONT_LEFT, lraw)) < 0) { LOG_ERROR("error setting left volume: %s", snd_strerror(err)); } if ((err = snd_mixer_selem_set_playback_volume(alsa.mixer_elem, SND_MIXER_SCHN_FRONT_RIGHT, rraw)) < 0) { LOG_ERROR("error setting right volume: %s", snd_strerror(err)); } } else { // set db directly LOG_DEBUG("setting vol dB [%ld..%ld]", alsa.mixer_min, alsa.mixer_max); if (setmax) { // set to 0dB if available as this should be max volume for music recored at max pcm values if (alsa.mixer_max >= 0 && alsa.mixer_min <= 0) { ldB = rdB = 0; } else { ldB = rdB = alsa.mixer_max; } } if ((err = snd_mixer_selem_set_playback_dB(alsa.mixer_elem, SND_MIXER_SCHN_FRONT_LEFT, 100 * ldB, 1)) < 0) { LOG_ERROR("error setting left volume: %s", snd_strerror(err)); } if ((err = snd_mixer_selem_set_playback_dB(alsa.mixer_elem, SND_MIXER_SCHN_FRONT_RIGHT, 100 * rdB, 1)) < 0) { LOG_ERROR("error setting right volume: %s", snd_strerror(err)); } } if ((err = snd_mixer_selem_get_playback_volume(alsa.mixer_elem, SND_MIXER_SCHN_FRONT_LEFT, &nleft)) < 0) { LOG_ERROR("error getting left vol: %s", snd_strerror(err)); } if ((err = snd_mixer_selem_get_playback_volume(alsa.mixer_elem, SND_MIXER_SCHN_FRONT_RIGHT, &nright)) < 0) { LOG_ERROR("error getting right vol: %s", snd_strerror(err)); } LOG_DEBUG("%s left: %3.1fdB -> %ld right: %3.1fdB -> %ld", alsa.volume_mixer_name, ldB, nleft, rdB, nright); } void set_volume(unsigned left, unsigned right) { float ldB, rdB; if (!alsa.volume_mixer_name) { LOG_DEBUG("setting internal gain left: %u right: %u", left, right); LOCK; output.gainL = left; output.gainR = right; UNLOCK; return; } else { LOCK; output.gainL = FIXED_ONE; output.gainR = FIXED_ONE; UNLOCK; } // convert 16.16 fixed point to dB ldB = 20 * log10( left / 65536.0F ); rdB = 20 * log10( right / 65536.0F ); set_mixer(false, ldB, rdB); } static void *alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) { va_list args; if ((loglevel >= lINFO && err == 0) || loglevel >= lDEBUG) { fprintf(stderr, "%s ALSA %s:%d ", logtime(), function, line); va_start(args, fmt); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); fflush(stderr); } return NULL; } static void alsa_close(void) { int err; if ((err = snd_pcm_close(pcmp)) < 0) { LOG_INFO("snd_pcm_close error: %s", snd_strerror(err)); } } bool test_open(const char *device, unsigned rates[], bool userdef_rates) { int err; snd_pcm_t *pcm; snd_pcm_hw_params_t *hw_params; hw_params = (snd_pcm_hw_params_t *) alloca(snd_pcm_hw_params_sizeof()); memset(hw_params, 0, snd_pcm_hw_params_sizeof()); // open device if ((err = snd_pcm_open(&pcm, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { LOG_ERROR("playback open error: %s", snd_strerror(err)); return false; } // get max params if ((err = snd_pcm_hw_params_any(pcm, hw_params)) < 0) { LOG_ERROR("hwparam init error: %s", snd_strerror(err)); return false; } // find supported sample rates to enable client side resampling of non supported rates if (!userdef_rates) { unsigned i, ind; unsigned ref[] TEST_RATES; for (i = 0, ind = 0; ref[i]; ++i) { if (snd_pcm_hw_params_test_rate(pcm, hw_params, ref[i], 0) == 0) { rates[ind++] = ref[i]; } else { LOG_DEBUG("sample rate %u not supported", ref[i]); } } } if ((err = snd_pcm_close(pcm)) < 0) { LOG_ERROR("snd_pcm_close error: %s", snd_strerror(err)); return false; } return true; } static bool pcm_probe(const char *device) { int err; snd_pcm_t *pcm; if ((err = snd_pcm_open(&pcm, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { return false; } if ((err = snd_pcm_close(pcm)) < 0) { LOG_ERROR("snd_pcm_close error: %s", snd_strerror(err)); } return true; } #if DSD static int alsa_open(const char *device, unsigned sample_rate, unsigned alsa_buffer, unsigned alsa_period, dsd_format outfmt) { #else static int alsa_open(const char *device, unsigned sample_rate, unsigned alsa_buffer, unsigned alsa_period) { #endif int err; snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_alloca(&hw_params); // close if already open if (pcmp) alsa_close(); // reset params alsa.rate = 0; #if DSD alsa.outfmt = PCM; #endif alsa.period_size = 0; strcpy(alsa.device, device); if (strlen(device) > MAX_DEVICE_LEN - 4 - 1) { LOG_ERROR("device name too long: %s", device); return -1; } LOG_INFO("opening device at: %u", sample_rate); bool retry; do { // open device if ((err = snd_pcm_open(&pcmp, alsa.device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { LOG_ERROR("playback open error: %s", snd_strerror(err)); return err; } // init params memset(hw_params, 0, snd_pcm_hw_params_sizeof()); if ((err = snd_pcm_hw_params_any(pcmp, hw_params)) < 0) { LOG_ERROR("hwparam init error: %s", snd_strerror(err)); return err; } // open hw: devices without resampling, if sample rate fails try plughw: with resampling bool hw = !strncmp(alsa.device, "hw:", 3); retry = false; if ((err = snd_pcm_hw_params_set_rate_resample(pcmp, hw_params, !hw)) < 0) { LOG_ERROR("resampling setup failed: %s", snd_strerror(err)); return err; } if ((err = snd_pcm_hw_params_set_rate(pcmp, hw_params, sample_rate, 0)) < 0) { if (hw) { strcpy(alsa.device + 4, device); memcpy(alsa.device, "plug", 4); LOG_INFO("reopening device %s in plug mode as %s for resampling", device, alsa.device); snd_pcm_close(pcmp); retry = true; } } } while (retry); // set access if (!alsa.mmap || snd_pcm_hw_params_set_access(pcmp, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) { if ((err = snd_pcm_hw_params_set_access(pcmp, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { LOG_ERROR("access type not available: %s", snd_strerror(err)); return err; } alsa.mmap = false; } // set the sample format #if DSD switch (outfmt) { case DSD_U8: alsa.format = SND_PCM_FORMAT_DSD_U8; break; case DSD_U16_LE: alsa.format = SND_PCM_FORMAT_DSD_U16_LE; break; case DSD_U16_BE: alsa.format = SND_PCM_FORMAT_DSD_U16_BE; break; case DSD_U32_LE: alsa.format = SND_PCM_FORMAT_DSD_U32_LE; break; case DSD_U32_BE: alsa.format = SND_PCM_FORMAT_DSD_U32_BE; break; case DOP_S24_LE: alsa.format = SND_PCM_FORMAT_S24_LE; break; case DOP_S24_3LE: alsa.format = SND_PCM_FORMAT_S24_3LE; break; default: alsa.format = alsa.pcmfmt; } #endif snd_pcm_format_t *fmt = alsa.format ? &alsa.format : (snd_pcm_format_t *)fmts; do { if (snd_pcm_hw_params_set_format(pcmp, hw_params, *fmt) >= 0) { LOG_INFO("opened device %s using format: %s sample rate: %u mmap: %u", alsa.device, snd_pcm_format_name(*fmt), sample_rate, alsa.mmap); alsa.format = *fmt; break; } if (alsa.format) { LOG_ERROR("unable to open audio device requested format: %s", snd_pcm_format_name(alsa.format)); return -1; } ++fmt; if (*fmt == SND_PCM_FORMAT_UNKNOWN) { LOG_ERROR("unable to open audio device with any supported format"); return -1; } } while (*fmt != SND_PCM_FORMAT_UNKNOWN); // set the output format to be used by _scale_and_pack switch(alsa.format) { case SND_PCM_FORMAT_S32_LE: output.format = S32_LE; break; case SND_PCM_FORMAT_S24_LE: output.format = S24_LE; break; case SND_PCM_FORMAT_S24_3LE: output.format = S24_3LE; break; case SND_PCM_FORMAT_S16_LE: output.format = S16_LE; break; #if DSD case SND_PCM_FORMAT_DSD_U32_LE: output.format = U32_LE; break; case SND_PCM_FORMAT_DSD_U32_BE: output.format = U32_BE; break; case SND_PCM_FORMAT_DSD_U16_LE: output.format = U16_LE; break; case SND_PCM_FORMAT_DSD_U16_BE: output.format = U16_BE; break; case SND_PCM_FORMAT_DSD_U8: output.format = U8; break; #endif default: break; } // set channels if ((err = snd_pcm_hw_params_set_channels (pcmp, hw_params, 2)) < 0) { LOG_ERROR("channel count not available: %s", snd_strerror(err)); return err; } // set period size - value of < 50 treated as period count, otherwise size in bytes if (alsa_period < 50) { unsigned count = alsa_period; if ((err = snd_pcm_hw_params_set_periods_near(pcmp, hw_params, &count, 0)) < 0) { LOG_ERROR("unable to set period count %s", snd_strerror(err)); return err; } } else { snd_pcm_uframes_t size = alsa_period; int dir = 0; if ((err = snd_pcm_hw_params_set_period_size_near(pcmp, hw_params, &size, &dir)) < 0) { LOG_ERROR("unable to set period size %s", snd_strerror(err)); return err; } } // set buffer size - value of < 500 treated as buffer time in ms, otherwise size in bytes if (alsa_buffer < 500) { unsigned time = alsa_buffer * 1000; int dir = 0; if ((err = snd_pcm_hw_params_set_buffer_time_near(pcmp, hw_params, &time, &dir)) < 0) { LOG_ERROR("unable to set buffer time %s", snd_strerror(err)); return err; } } else { snd_pcm_uframes_t size = alsa_buffer; if ((err = snd_pcm_hw_params_set_buffer_size_near(pcmp, hw_params, &size)) < 0) { LOG_ERROR("unable to set buffer size %s", snd_strerror(err)); return err; } } // get period_size if ((err = snd_pcm_hw_params_get_period_size(hw_params, &alsa.period_size, 0)) < 0) { LOG_ERROR("unable to get period size: %s", snd_strerror(err)); return err; } // get buffer_size if ((err = snd_pcm_hw_params_get_buffer_size(hw_params, &alsa.buffer_size)) < 0) { LOG_ERROR("unable to get buffer size: %s", snd_strerror(err)); return err; } LOG_INFO("buffer: %u period: %u -> buffer size: %u period size: %u", alsa_buffer, alsa_period, alsa.buffer_size, alsa.period_size); // ensure we have two buffer sizes of samples before starting output output.start_frames = alsa.buffer_size * 2; // create an intermediate buffer for non mmap case for all but NATIVE_FORMAT // this is used to pack samples into the output format before calling writei if (!alsa.mmap && !alsa.write_buf && alsa.format != NATIVE_FORMAT) { alsa.write_buf = malloc(alsa.buffer_size * BYTES_PER_FRAME); if (!alsa.write_buf) { LOG_ERROR("unable to malloc write_buf"); return -1; } } // set params if ((err = snd_pcm_hw_params(pcmp, hw_params)) < 0) { LOG_ERROR("unable to set hw params: %s", snd_strerror(err)); return err; } // dump info if (loglevel == lSDEBUG) { static snd_output_t *debug_output; snd_output_stdio_attach(&debug_output, stderr, 0); snd_pcm_dump(pcmp, debug_output); } // this indicates we have opened the device ok alsa.rate = sample_rate; #if DSD alsa.outfmt = outfmt; #endif return 0; } static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t offset; void *outputptr; s32_t *inputptr; int err; if (alsa.mmap) { snd_pcm_uframes_t alsa_frames = (snd_pcm_uframes_t)out_frames; snd_pcm_avail_update(pcmp); if ((err = snd_pcm_mmap_begin(pcmp, &areas, &offset, &alsa_frames)) < 0) { LOG_WARN("error from mmap_begin: %s", snd_strerror(err)); return -1; } out_frames = (frames_t)alsa_frames; } if (!silence) { // applying cross fade is delayed until this point as mmap_begin can change out_frames if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) { _apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr); } } inputptr = (s32_t *) (silence ? silencebuf : outputbuf->readp); IF_DSD( if (output.outfmt != PCM) { if (silence) { inputptr = (s32_t *) silencebuf_dsd; } if (output.outfmt == DOP || output.outfmt == DOP_S24_LE || output.outfmt == DOP_S24_3LE) update_dop((u32_t *) inputptr, out_frames, output.invert && !silence); else if (output.invert && !silence) dsd_invert((u32_t *) inputptr, out_frames); } ) if (alsa.mmap || alsa.format != NATIVE_FORMAT) { outputptr = alsa.mmap ? (areas[0].addr + (areas[0].first + offset * areas[0].step) / 8) : alsa.write_buf; _scale_and_pack_frames(outputptr, inputptr, out_frames, gainL, gainR, flags, output.format); } else { outputptr = (void *)inputptr; if (!silence) { _apply_gain(outputbuf, out_frames, gainL, gainR, flags); } } if (alsa.mmap) { snd_pcm_sframes_t w = snd_pcm_mmap_commit(pcmp, offset, out_frames); if (w < 0 || w != out_frames) { LOG_WARN("mmap_commit error"); return -1; } } else { snd_pcm_sframes_t w = snd_pcm_writei(pcmp, outputptr, out_frames); if (w < 0) { //if (w != -EAGAIN && ((err = snd_pcm_recover(pcmp, w, 1)) < 0)) { if (((err = snd_pcm_recover(pcmp, w, 1)) < 0)) { static unsigned recover_count = 0; LOG_WARN("recover failed: %s [%u]", snd_strerror(err), ++recover_count); if (recover_count >= 10) { recover_count = 0; alsa_close(); pcmp = NULL; } } return -1; } else { if (w != out_frames) { LOG_WARN("writei only wrote %u of %u", w, out_frames); } out_frames = w; } } return (int)out_frames; } static void *output_thread(void *arg) { bool start = true; bool output_off = (output.state == OUTPUT_OFF); bool probe_device = (arg != NULL); int err; while (running) { // disabled output - player is off while (output_off) { usleep(100000); LOCK; output_off = (output.state == OUTPUT_OFF); UNLOCK; if (!running) return 0; } // wait until device returns - to allow usb audio devices to be turned off if (probe_device) { while (!pcm_probe(output.device)) { LOG_DEBUG("waiting for device %s to return", output.device); sleep(5); } probe_device = false; } #if DSD if (!pcmp || alsa.rate != output.current_sample_rate || alsa.outfmt != output.outfmt ) { #else if (!pcmp || alsa.rate != output.current_sample_rate) { #endif #if GPIO // Wake up amp if (gpio_active) { relay(1); } if (power_script != NULL) { relay_script(1); } #endif LOG_INFO("open output device: %s", output.device); LOCK; // FIXME - some alsa hardware requires opening twice for a new sample rate to work // this is a workaround which should be removed if (alsa.reopen) { #if DSD alsa_open(output.device, output.current_sample_rate, output.buffer, output.period, output.outfmt); #else alsa_open(output.device, output.current_sample_rate, output.buffer, output.period); #endif } #if DSD if (!!alsa_open(output.device, output.current_sample_rate, output.buffer, output.period, output.outfmt)) { #else if (!!alsa_open(output.device, output.current_sample_rate, output.buffer, output.period)) { #endif output.error_opening = true; UNLOCK; sleep(5); continue; } output.error_opening = false; start = true; UNLOCK; } snd_pcm_state_t state = snd_pcm_state(pcmp); if (state == SND_PCM_STATE_XRUN) { LOG_INFO("XRUN"); if ((err = snd_pcm_recover(pcmp, -EPIPE, 1)) < 0) { LOG_INFO("XRUN recover failed: %s", snd_strerror(err)); usleep(10000); } start = true; continue; } else if (state == SND_PCM_STATE_SUSPENDED) { if ((err = snd_pcm_recover(pcmp, -ESTRPIPE, 1)) < 0) { LOG_INFO("SUSPEND recover failed: %s", snd_strerror(err)); } } else if (state == SND_PCM_STATE_DISCONNECTED) { LOG_INFO("Device %s no longer available", output.device); alsa_close(); pcmp = NULL; probe_device = true; continue; } snd_pcm_sframes_t avail = snd_pcm_avail_update(pcmp); if (avail < 0) { if ((err = snd_pcm_recover(pcmp, avail, 1)) < 0) { if (err == -ENODEV) { LOG_INFO("Device %s no longer available", output.device); alsa_close(); pcmp = NULL; probe_device = true; continue; } LOG_WARN("recover failed: %s", snd_strerror(err)); } start = true; continue; } if (avail < alsa.period_size) { if (start) { if (alsa.mmap && ((err = snd_pcm_start(pcmp)) < 0)) { if ((err = snd_pcm_recover(pcmp, err, 1)) < 0) { if (err == -ENODEV) { LOG_INFO("Device %s no longer available", output.device); alsa_close(); pcmp = NULL; probe_device = true; continue; } LOG_INFO("start error: %s", snd_strerror(err)); usleep(10000); } } else { start = false; } } else { usleep(10000); if ((err = snd_pcm_wait(pcmp, 1000)) <= 0) { if ( err == 0 ) { LOG_INFO("pcm wait timeout"); } if ((err = snd_pcm_recover(pcmp, err, 1)) < 0) { LOG_INFO("pcm wait error: %s", snd_strerror(err)); } start = true; } } continue; } // restrict avail to within sensible limits as alsa drivers can return erroneous large values // in writei mode restrict to period_size due to size of write_buf if (alsa.mmap) { avail = min(avail, alsa.buffer_size); } else { avail = min(avail, alsa.period_size); } // avoid spinning in cases where wait returns but no bytes available (seen with pulse audio) if (avail == 0) { LOG_SDEBUG("avail 0 - sleeping"); usleep(10000); continue; } LOCK; // turn off if requested if (output.state == OUTPUT_OFF) { UNLOCK; LOG_INFO("disabling output"); alsa_close(); pcmp = NULL; output_off = true; vis_stop(); #if GPIO // Put Amp to Sleep if (gpio_active){ relay(0); } if (power_script != NULL ){ relay_script(0); } #endif continue; } // measure output delay snd_pcm_sframes_t delay; if ((err = snd_pcm_delay(pcmp, &delay)) < 0) { if (err == -EPIPE) { // EPIPE indicates underrun - attempt to recover UNLOCK; continue; } else if (err == -EIO) { // EIO can occur with non existant pulse server UNLOCK; LOG_SDEBUG("snd_pcm_delay returns: EIO - sleeping"); usleep(100000); continue; } else { LOG_DEBUG("snd_pcm_delay returns: %d", err); } } else { output.device_frames = delay; output.updated = gettime_ms(); output.frames_played_dmp = output.frames_played; } // process frames frames_t wrote = _output_frames(avail); UNLOCK; // some output devices such as alsa null refuse any data, avoid spinning if (!wrote) { LOG_SDEBUG("wrote 0 - sleeping"); usleep(10000); } } return 0; } int mixer_init_alsa(const char *device, const char *mixer, int mixer_index) { int err; snd_mixer_selem_id_t *sid; if ((err = snd_mixer_open(&alsa.mixer_handle, 0)) < 0) { LOG_ERROR("open error: %s", snd_strerror(err)); return -1; } if ((err = snd_mixer_attach(alsa.mixer_handle, device)) < 0) { LOG_ERROR("attach error: %s", snd_strerror(err)); snd_mixer_close(alsa.mixer_handle); return -1; } if ((err = snd_mixer_selem_register(alsa.mixer_handle, NULL, NULL)) < 0) { LOG_ERROR("register error: %s", snd_strerror(err)); snd_mixer_close(alsa.mixer_handle); return -1; } if ((err = snd_mixer_load(alsa.mixer_handle)) < 0) { LOG_ERROR("load error: %s", snd_strerror(err)); snd_mixer_close(alsa.mixer_handle); return -1; } snd_mixer_selem_id_alloca(&sid); snd_mixer_selem_id_set_index(sid, mixer_index); snd_mixer_selem_id_set_name(sid, mixer); if ((alsa.mixer_elem = snd_mixer_find_selem(alsa.mixer_handle, sid)) == NULL) { LOG_ERROR("error find selem %s", alsa.mixer_handle); snd_mixer_close(alsa.mixer_handle); return -1; } if (snd_mixer_selem_has_playback_switch(alsa.mixer_elem)) { snd_mixer_selem_set_playback_switch_all(alsa.mixer_elem, 1); // unmute } err = snd_mixer_selem_get_playback_dB_range(alsa.mixer_elem, &alsa.mixer_min, &alsa.mixer_max); if (err < 0 || alsa.mixer_max - alsa.mixer_min < 1000 || alsa.mixer_linear) { alsa.mixer_linear = 1; // unable to get db range or range is less than 10dB - ignore and set using raw values if ((err = snd_mixer_selem_get_playback_volume_range(alsa.mixer_elem, &alsa.mixer_min, &alsa.mixer_max)) < 0) { LOG_ERROR("Unable to get volume raw range"); return -1; } } return 0; } static pthread_t thread; void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned rt_priority, unsigned idle, char *mixer_device, char *volume_mixer, bool mixer_unmute, bool mixer_linear) { unsigned alsa_buffer = ALSA_BUFFER_TIME; unsigned alsa_period = ALSA_PERIOD_COUNT; char *alsa_sample_fmt = NULL; bool alsa_mmap = true; bool alsa_reopen = false; char *volume_mixer_name = next_param(volume_mixer, ','); char *volume_mixer_index = next_param(NULL, ','); char *t = next_param(params, ':'); char *c = next_param(NULL, ':'); char *s = next_param(NULL, ':'); char *m = next_param(NULL, ':'); char *r = next_param(NULL, ':'); if (t) alsa_buffer = atoi(t); if (c) alsa_period = atoi(c); if (s) alsa_sample_fmt = s; if (m) alsa_mmap = atoi(m); if (r) alsa_reopen = atoi(r); loglevel = level; LOG_INFO("init output"); memset(&output, 0, sizeof(output)); alsa.mmap = alsa_mmap; alsa.write_buf = NULL; #if DSD alsa.pcmfmt = 0; #else alsa.format = 0; #endif alsa.reopen = alsa_reopen; alsa.mixer_handle = NULL; alsa.ctl = ctl4device(device); alsa.mixer_ctl = mixer_device ? ctl4device(mixer_device) : alsa.ctl; alsa.volume_mixer_name = volume_mixer_name; alsa.mixer_linear = mixer_linear; output.format = 0; output.buffer = alsa_buffer; output.period = alsa_period; output.start_frames = 0; output.write_cb = &_write_frames; output.rate_delay = rate_delay; if (alsa_sample_fmt) { #if DSD if (!strcmp(alsa_sample_fmt, "32")) alsa.pcmfmt = SND_PCM_FORMAT_S32_LE; if (!strcmp(alsa_sample_fmt, "24")) alsa.pcmfmt = SND_PCM_FORMAT_S24_LE; if (!strcmp(alsa_sample_fmt, "24_3")) alsa.pcmfmt = SND_PCM_FORMAT_S24_3LE; if (!strcmp(alsa_sample_fmt, "16")) alsa.pcmfmt = SND_PCM_FORMAT_S16_LE; #else if (!strcmp(alsa_sample_fmt, "32")) alsa.format = SND_PCM_FORMAT_S32_LE; if (!strcmp(alsa_sample_fmt, "24")) alsa.format = SND_PCM_FORMAT_S24_LE; if (!strcmp(alsa_sample_fmt, "24_3")) alsa.format = SND_PCM_FORMAT_S24_3LE; if (!strcmp(alsa_sample_fmt, "16")) alsa.format = SND_PCM_FORMAT_S16_LE; #endif } LOG_INFO("requested alsa_buffer: %u alsa_period: %u format: %s mmap: %u", output.buffer, output.period, alsa_sample_fmt ? alsa_sample_fmt : "any", alsa.mmap); snd_lib_error_set_handler((snd_lib_error_handler_t)alsa_error_handler); output_init_common(level, device, output_buf_size, rates, idle); if (volume_mixer_name) { if (mixer_init_alsa(alsa.mixer_ctl, alsa.volume_mixer_name, volume_mixer_index ? atoi(volume_mixer_index) : 0) < 0) { LOG_ERROR("Initialization of mixer failed, reverting to software volume"); alsa.mixer_handle = NULL; alsa.volume_mixer_name = NULL; } } if (mixer_unmute && alsa.volume_mixer_name) { set_mixer(true, 0, 0); alsa.volume_mixer_name = NULL; } #if LINUX // RT linux - aim to avoid pagefaults by locking memory: // https://rt.wiki.kernel.org/index.php/Threaded_RT-application_with_memory_locking_and_stack_handling_example if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) { LOG_INFO("unable to lock memory: %s", strerror(errno)); } else { LOG_INFO("memory locked"); } #ifdef __GLIBC__ mallopt(M_TRIM_THRESHOLD, -1); mallopt(M_MMAP_MAX, 0); LOG_INFO("glibc detected using mallopt"); #endif touch_memory(silencebuf, MAX_SILENCE_FRAMES * BYTES_PER_FRAME); touch_memory(outputbuf->buf, outputbuf->size); #endif // start output thread pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE); pthread_create(&thread, &attr, output_thread, rates[0] ? "probe" : NULL); pthread_attr_destroy(&attr); // try to set this thread to real-time scheduler class, only works as root or if user has permission struct sched_param param; param.sched_priority = rt_priority; if (pthread_setschedparam(thread, SCHED_FIFO, ¶m) != 0) { LOG_DEBUG("unable to set output sched fifo: %s", strerror(errno)); } else { LOG_DEBUG("set output sched fifo rt: %u", param.sched_priority); } } void output_close_alsa(void) { LOG_INFO("close output"); LOCK; running = false; UNLOCK; pthread_join(thread, NULL); if (alsa.write_buf) free(alsa.write_buf); if (alsa.ctl) free(alsa.ctl); if (alsa.mixer_ctl) free(alsa.mixer_ctl); if (alsa.mixer_handle != NULL) snd_mixer_close(alsa.mixer_handle); output_close_common(); } #endif // ALSA squeezelite-1.9.9-1395+git20220104.874e4f9/output_pa.c000066400000000000000000000411571416534420200212700ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // Portaudio output #include "squeezelite.h" #if PORTAUDIO #include #if WIN #ifndef PA18API #include #endif #define snprintf _snprintf #endif #if OSX && !defined(OSXPPC) #include #endif #if PA18API typedef int PaDeviceIndex; typedef double PaTime; typedef struct PaStreamParameters { PaDeviceIndex device; int channelCount; PaSampleFormat sampleFormat; PaTime suggestedLatency; } PaStreamParameters; static int paContinue=0; /* Signal that the stream should continue invoking the callback and processing audio. */ static int paComplete=1; /* Signal that the stream should stop invoking the callback and finish once all output */ /* samples have played. */ static unsigned paFramesPerBuffer = 4096; static unsigned paNumberOfBuffers = 4; #endif /* PA18API */ // ouput device static struct { unsigned rate; PaStream *stream; } pa; static log_level loglevel; static bool running = true; extern struct outputstate output; extern struct buffer *outputbuf; #define LOCK mutex_lock(outputbuf->mutex) #define UNLOCK mutex_unlock(outputbuf->mutex) extern u8_t *silencebuf; #if DSD extern u8_t *silencebuf_dsd; #endif void list_devices(void) { PaError err; int i; if ((err = Pa_Initialize()) != paNoError) { LOG_WARN("error initialising port audio: %s", Pa_GetErrorText(err)); return; } printf("Output devices:\n"); #ifndef PA18API for (i = 0; i < Pa_GetDeviceCount(); ++i) { if (Pa_GetDeviceInfo(i)->maxOutputChannels > 1) { printf(" %i - %s [%s]\n", i, Pa_GetDeviceInfo(i)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(i)->hostApi)->name); } #else for (i = 0; i < Pa_CountDevices(); ++i) { printf(" %i - %s\n", i, Pa_GetDeviceInfo(i)->name); #endif } printf("\n"); if ((err = Pa_Terminate()) != paNoError) { LOG_WARN("error closing port audio: %s", Pa_GetErrorText(err)); } } void set_volume(unsigned left, unsigned right) { LOG_DEBUG("setting internal gain left: %u right: %u", left, right); LOCK; output.gainL = left; output.gainR = right; UNLOCK; } static int pa_device_id(const char *device) { int len = strlen(device); int i; if (!strncmp(device, "default", 7)) { #ifndef PA18API return Pa_GetDefaultOutputDevice(); #else return Pa_GetDefaultOutputDeviceID(); #endif } if (len >= 1 && len <= 2 && device[0] >= '0' && device[0] <= '9') { return atoi(device); } #ifndef PA18API #define DEVICE_ID_MAXLEN 256 for (i = 0; i < Pa_GetDeviceCount(); ++i) { char tmp[DEVICE_ID_MAXLEN]; snprintf(tmp, DEVICE_ID_MAXLEN, "%s [%s]", Pa_GetDeviceInfo(i)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(i)->hostApi)->name); if (!strncmp(tmp, device, len)) { #else for (i = 0; i < Pa_CountDevices(); ++i) { if (!strncmp(Pa_GetDeviceInfo(i)->name, device, len)) { #endif return i; } } return -1; } #ifndef PA18API static int pa_callback(const void *pa_input, void *pa_output, unsigned long pa_frames_wanted, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData); #else static int pa_callback(void *pa_input, void *pa_output, unsigned long pa_frames_wanted, PaTimestamp outTime, void *userData); #endif bool test_open(const char *device, unsigned rates[], bool userdef_rates) { PaStreamParameters outputParameters; PaError err; unsigned ref[] TEST_RATES; int device_id, i, ind; #if WIN PaWasapiStreamInfo wasapiInfo; const PaDeviceInfo * paDeviceInfo; const PaHostApiInfo *paHostApiInfo; #endif if ((device_id = pa_device_id(device)) == -1) { LOG_INFO("device %s not found", device); return false; } outputParameters.device = device_id; outputParameters.channelCount = 2; outputParameters.sampleFormat = paInt32; #ifndef PA18API outputParameters.suggestedLatency = output.latency ? (double)output.latency/(double)1000 : Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; #if WIN paDeviceInfo = Pa_GetDeviceInfo( outputParameters.device ); paHostApiInfo = Pa_GetHostApiInfo ( paDeviceInfo->hostApi ); if ( paHostApiInfo != NULL ) { if ( paHostApiInfo->type == paWASAPI ) { /* Use exclusive mode for WasApi device, default is shared */ if (output.pa_hostapi_option == 1) { wasapiInfo.size = sizeof(PaWasapiStreamInfo); wasapiInfo.hostApiType = paWASAPI; wasapiInfo.version = 1; wasapiInfo.flags = paWinWasapiExclusive; outputParameters.hostApiSpecificStreamInfo = &wasapiInfo; LOG_INFO("opening WASAPI device in exclusive mode"); } } } #endif /* WIN */ #endif // check supported sample rates // Note use Pa_OpenStream as it appears more reliable than Pa_IsFormatSupported on some windows apis for (i = 0, ind = 0; ref[i]; ++i) { #ifndef PA18API err = Pa_OpenStream(&pa.stream, NULL, &outputParameters, (double)ref[i], paFramesPerBufferUnspecified, paNoFlag, pa_callback, NULL); #else err = Pa_OpenStream(&pa.stream, paNoDevice, 0, 0, NULL, outputParameters.device, outputParameters.channelCount, outputParameters.sampleFormat, NULL, (double)ref[i], paFramesPerBuffer, paNumberOfBuffers, paNoFlag, pa_callback, NULL); #endif switch (err) { case paInvalidSampleRate: LOG_DEBUG("sample rate %u not supported", ref[i]); continue; #if WIN #ifndef PA18API /* Ignore these errors for device probe */ case paUnanticipatedHostError: continue; case paInvalidDevice: continue; #endif #endif case paNoError: Pa_CloseStream(pa.stream); if (!userdef_rates) { rates[ind++] = ref[i]; } continue; default: /* Any other error is a failure */ LOG_WARN("error opening portaudio stream: %s", Pa_GetErrorText(err)); return false; } } if (!rates[0] && !userdef_rates) { LOG_WARN("no available rate found"); return false; } pa.stream = NULL; return true; } static void pa_stream_finished(void *userdata) { if (running) { LOG_INFO("stream finished"); LOCK; output.pa_reopen = true; wake_controller(); UNLOCK; } } static thread_type monitor_thread; bool monitor_thread_running = false; static void *pa_monitor() { bool output_off; LOCK; if (monitor_thread_running) { LOG_DEBUG("monitor thread already running"); UNLOCK; return 0; } LOG_DEBUG("start monitor thread"); monitor_thread_running = true; output_off = (output.state == OUTPUT_OFF); while (monitor_thread_running) { if (output_off) { if (output.state != OUTPUT_OFF) { LOG_INFO("output on"); break; } } else { // this is a hack to partially support hot plugging of devices // we rely on terminating and reinitalising PA to get an updated list of devices and use name for output.device LOG_INFO("probing device %s", output.device); Pa_Terminate(); Pa_Initialize(); pa.stream = NULL; if (pa_device_id(output.device) != -1) { LOG_INFO("device reopen"); break; } } UNLOCK; sleep(output_off ? 1 : 5); LOCK; } LOG_DEBUG("end monitor thread"); monitor_thread_running = false; pa.stream = NULL; _pa_open(); UNLOCK; return 0; } void _pa_open(void) { PaStreamParameters outputParameters; PaError err = paNoError; int device_id; #if WIN PaWasapiStreamInfo wasapiInfo; const PaDeviceInfo * paDeviceInfo; const PaHostApiInfo *paHostApiInfo; #endif if (pa.stream) { if ((err = Pa_CloseStream(pa.stream)) != paNoError) { LOG_WARN("error closing stream: %s", Pa_GetErrorText(err)); } } if (output.state == OUTPUT_OFF) { // we get called when transitioning to OUTPUT_OFF to create the probe thread // set err to avoid opening device and logging messages err = 1; } else if ((device_id = pa_device_id(output.device)) == -1) { LOG_INFO("device %s not found", output.device); err = 1; } else { outputParameters.device = device_id; outputParameters.channelCount = 2; outputParameters.sampleFormat = paInt32; #ifndef PA18API outputParameters.suggestedLatency = output.latency ? (double)output.latency/(double)1000 : Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; #endif #if OSX && !defined(OSXPPC) /* enable pro mode which aims to avoid resampling if possible */ /* command line controls pa_hostapi_option which is -1 if not specified, 0 or 1 - choose playnice if -1 or 1 */ PaMacCoreStreamInfo macInfo; unsigned long streamInfoFlags; if (output.pa_hostapi_option) { LOG_INFO("opening device in PlayNice mode"); streamInfoFlags = paMacCorePlayNice; } else { LOG_INFO("opening device in Pro mode"); streamInfoFlags = paMacCorePro; } PaMacCore_SetupStreamInfo(&macInfo, streamInfoFlags); outputParameters.hostApiSpecificStreamInfo = &macInfo; #endif #if WIN paDeviceInfo = Pa_GetDeviceInfo( outputParameters.device ); paHostApiInfo = Pa_GetHostApiInfo ( paDeviceInfo->hostApi ); if ( paHostApiInfo != NULL ) { if ( paHostApiInfo->type == paWASAPI ) { /* Use exclusive mode for WasApi device, default is shared */ if (output.pa_hostapi_option == 1) { wasapiInfo.size = sizeof(PaWasapiStreamInfo); wasapiInfo.hostApiType = paWASAPI; wasapiInfo.version = 1; wasapiInfo.flags = paWinWasapiExclusive; outputParameters.hostApiSpecificStreamInfo = &wasapiInfo; LOG_INFO("opening WASAPI device in exclusive mode"); } } } #endif } if (!err && #ifndef PA18API (err = Pa_OpenStream(&pa.stream, NULL, &outputParameters, (double)output.current_sample_rate, paFramesPerBufferUnspecified, paPrimeOutputBuffersUsingStreamCallback | paDitherOff, pa_callback, NULL)) != paNoError) { LOG_WARN("error opening device %i - %s [%s] : %s", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(outputParameters.device)->hostApi)->name, Pa_GetErrorText(err)); #else (err = Pa_OpenStream(&pa.stream, paNoDevice, 0, 0, NULL, outputParameters.device, outputParameters.channelCount, outputParameters.sampleFormat, NULL, (double)output.current_sample_rate, paFramesPerBuffer, paNumberOfBuffers, paDitherOff, pa_callback, NULL)) != paNoError) { LOG_WARN("error opening device %i - %s : %s", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name, Pa_GetErrorText(err)); #endif } if (!err) { #ifndef PA18API LOG_INFO("opened device %i - %s [%s] at %u latency %u ms", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(outputParameters.device)->hostApi)->name, (unsigned int)Pa_GetStreamInfo(pa.stream)->sampleRate, (unsigned int)(Pa_GetStreamInfo(pa.stream)->outputLatency * 1000)); #else LOG_INFO("opened device %i - %s at %u fpb %u nbf %u", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name, (unsigned int)output.current_sample_rate, paFramesPerBuffer, paNumberOfBuffers); #endif pa.rate = output.current_sample_rate; #ifndef PA18API if ((err = Pa_SetStreamFinishedCallback(pa.stream, pa_stream_finished)) != paNoError) { LOG_WARN("error setting finish callback: %s", Pa_GetErrorText(err)); } UNLOCK; // StartStream can call pa_callback in a sychronised thread on freebsd, remove lock while it is called #endif if ((err = Pa_StartStream(pa.stream)) != paNoError) { LOG_WARN("error starting stream: %s", Pa_GetErrorText(err)); } #ifndef PA18API LOCK; #endif } if (err && !monitor_thread_running) { vis_stop(); // create a thread to check for output state change or device return #if LINUX || OSX || FREEBSD pthread_create(&monitor_thread, NULL, pa_monitor, NULL); #endif #if WIN monitor_thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&pa_monitor, NULL, 0, NULL); #endif } output.error_opening = !!err; } static u8_t *optr; static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) { if (!silence) { if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) { _apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr); } if (gainL != FIXED_ONE || gainR!= FIXED_ONE) { _apply_gain(outputbuf, out_frames, gainL, gainR, flags); } IF_DSD( if (output.outfmt == DOP) { update_dop((u32_t *) outputbuf->readp, out_frames, output.invert); } else if (output.outfmt != PCM && output.invert) dsd_invert((u32_t *) outputbuf->readp, out_frames); ) memcpy(optr, outputbuf->readp, out_frames * BYTES_PER_FRAME); } else { u8_t *buf = silencebuf; IF_DSD( if (output.outfmt != PCM) { buf = silencebuf_dsd; update_dop((u32_t *) buf, out_frames, false); // don't invert silence } ) memcpy(optr, buf, out_frames * BYTES_PER_FRAME); } optr += out_frames * BYTES_PER_FRAME; return (int)out_frames; } #ifndef PA18API static int pa_callback(const void *pa_input, void *pa_output, unsigned long pa_frames_wanted, const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags statusFlags, void *userData) { #else static int pa_callback(void *pa_input, void *pa_output, unsigned long pa_frames_wanted,PaTimestamp outTime, void *userData) { #endif int ret; frames_t frames; optr = (u8_t *)pa_output; LOCK; #ifndef PA18API if (time_info->outputBufferDacTime > time_info->currentTime) { // workaround for wdm-ks which can return outputBufferDacTime with a different epoch output.device_frames = (unsigned)((time_info->outputBufferDacTime - time_info->currentTime) * output.current_sample_rate); } else { output.device_frames = 0; } #else output.device_frames = 0; #endif output.updated = gettime_ms(); output.frames_played_dmp = output.frames_played; do { frames = _output_frames(pa_frames_wanted); pa_frames_wanted -= frames; } while (pa_frames_wanted > 0 && frames != 0); if (pa_frames_wanted > 0) { LOG_DEBUG("pad with silence"); memset(optr, 0, pa_frames_wanted * BYTES_PER_FRAME); } if (output.state == OUTPUT_OFF) { LOG_INFO("output off"); ret = paComplete; } else if (pa.rate != output.current_sample_rate) { ret = paComplete; } else { ret = paContinue; } UNLOCK; #ifdef PA18API if ( ret == paComplete ) pa_stream_finished (userData); #endif return ret; } void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) { PaError err; #ifndef PA18API unsigned latency = 0; int pa_hostapi_option = -1; #else unsigned pa_frames = 0; unsigned pa_nbufs = 0; #endif /* PA18API */ #ifndef PA18API char *l = next_param(params, ':'); char *p = next_param(NULL, ':'); if (l) latency = (unsigned)atoi(l); if (p) pa_hostapi_option = atoi(p); #else char *t = next_param(params, ':'); char *c = next_param(NULL, ':'); if (t) pa_frames = atoi(t); if (c) pa_nbufs = atoi(c); #endif loglevel = level; LOG_INFO("init output"); memset(&output, 0, sizeof(output)); #ifndef PA18API output.latency = latency; output.pa_hostapi_option = pa_hostapi_option; #else if ( pa_frames != 0 ) paFramesPerBuffer = pa_frames; if ( pa_nbufs != 0 ) paNumberOfBuffers = pa_nbufs; #endif /* PA18API */ output.format = 0; output.start_frames = 0; output.write_cb = &_write_frames; output.rate_delay = rate_delay; pa.stream = NULL; #ifndef PA18API LOG_INFO("requested latency: %u", output.latency); #endif if ((err = Pa_Initialize()) != paNoError) { LOG_WARN("error initialising port audio: %s", Pa_GetErrorText(err)); exit(0); } output_init_common(level, device, output_buf_size, rates, idle); LOCK; _pa_open(); UNLOCK; } void output_close_pa(void) { PaError err; LOG_INFO("close output"); LOCK; running = false; monitor_thread_running = false; if (pa.stream) { if ((err = Pa_AbortStream(pa.stream)) != paNoError) { LOG_WARN("error closing stream: %s", Pa_GetErrorText(err)); } } if ((err = Pa_Terminate()) != paNoError) { LOG_WARN("error closing port audio: %s", Pa_GetErrorText(err)); } UNLOCK; output_close_common(); } #endif // PORTAUDIO squeezelite-1.9.9-1395+git20220104.874e4f9/output_pack.c000066400000000000000000000311331416534420200215770ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // Scale and pack functions #include "squeezelite.h" #define MAX_SCALESAMPLE 0x7fffffffffffLL #define MIN_SCALESAMPLE -MAX_SCALESAMPLE // inlining these on windows prevents them being linkable... #if !WIN inline #endif s32_t gain(s32_t gain, s32_t sample) { s64_t res = (s64_t)gain * (s64_t)sample; if (res > MAX_SCALESAMPLE) res = MAX_SCALESAMPLE; if (res < MIN_SCALESAMPLE) res = MIN_SCALESAMPLE; return (s32_t) (res >> 16); } #if !WIN inline #endif s32_t to_gain(float f) { return (s32_t)(f * 65536.0F); } void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, u8_t flags, output_format format) { // in-place copy input samples if mono/combined is used (never happens with DSD active) if ((flags & MONO_LEFT) && (flags & MONO_RIGHT)) { s32_t *ptr = inputptr; frames_t count = cnt; while (count--) { // use 64 bits integer for purists but should really not care *ptr = *(ptr + 1) = ((s64_t) *ptr + (s64_t) *(ptr + 1)) / 2; ptr += 2; } } else if (flags & MONO_RIGHT) { s32_t *ptr = inputptr + 1; frames_t count = cnt; while (count--) { *(ptr - 1) = *ptr; ptr += 2; } } else if (flags & MONO_LEFT) { s32_t *ptr = inputptr; frames_t count = cnt; while (count--) { *(ptr + 1) = *ptr; ptr += 2; } } switch(format) { #if DSD case U32_LE: { #if SL_LITTLE_ENDIAN memcpy(outputptr, inputptr, cnt * BYTES_PER_FRAME); #else u32_t *optr = (u32_t *)(void *)outputptr; while (cnt--) { s32_t lsample = *(inputptr++); s32_t rsample = *(inputptr++); *(optr++) = (lsample & 0xff000000) >> 24 | (lsample & 0x00ff0000) >> 8 | (lsample & 0x0000ff00) << 8 | (lsample & 0x000000ff) << 24; *(optr++) = (rsample & 0xff000000) >> 24 | (rsample & 0x00ff0000) >> 8 | (rsample & 0x0000ff00) << 8 | (rsample & 0x000000ff) << 24; } #endif } break; case U32_BE: { #if SL_LITTLE_ENDIAN u32_t *optr = (u32_t *)(void *)outputptr; while (cnt--) { s32_t lsample = *(inputptr++); s32_t rsample = *(inputptr++); *(optr++) = (lsample & 0xff000000) >> 24 | (lsample & 0x00ff0000) >> 8 | (lsample & 0x0000ff00) << 8 | (lsample & 0x000000ff) << 24; *(optr++) = (rsample & 0xff000000) >> 24 | (rsample & 0x00ff0000) >> 8 | (rsample & 0x0000ff00) << 8 | (rsample & 0x000000ff) << 24; } #else memcpy(outputptr, inputptr, cnt * BYTES_PER_FRAME); #endif } break; case U16_LE: { u32_t *optr = (u32_t *)(void *)outputptr; #if SL_LITTLE_ENDIAN while (cnt--) { *(optr++) = (*(inputptr) >> 16 & 0x0000ffff) | (*(inputptr + 1) & 0xffff0000); inputptr += 2; } #else while (cnt--) { s32_t lsample = *(inputptr++); s32_t rsample = *(inputptr++); *(optr++) = (lsample & 0x00ff0000) << 8 | (lsample & 0xff000000) >> 8 | (rsample & 0x00ff0000) >> 8 | (rsample & 0xff000000) >> 24; } #endif } break; case U16_BE: { u32_t *optr = (u32_t *)(void *)outputptr; #if SL_LITTLE_ENDIAN while (cnt--) { s32_t lsample = *(inputptr++); s32_t rsample = *(inputptr++); *(optr++) = (lsample & 0xff000000) >> 24 | (lsample & 0x00ff0000) >> 8 | (rsample & 0xff000000) >> 8 | (rsample & 0x00ff0000) << 8; } #else while (cnt--) { *(optr++) = (*(inputptr) & 0xffff0000) | (*(inputptr + 1) >> 16 & 0x0000ffff); inputptr += 2; } #endif } break; case U8: { u16_t *optr = (u16_t *)(void *)outputptr; #if SL_LITTLE_ENDIAN while (cnt--) { *(optr++) = (u16_t)((*(inputptr) >> 24 & 0x000000ff) | (*(inputptr + 1) >> 16 & 0x0000ff00)); inputptr += 2; } #else while (cnt--) { *(optr++) = (u16_t)((*(inputptr) >> 16 & 0x0000ff00) | (*(inputptr + 1) >> 24 & 0x000000ff)); inputptr += 2; } #endif } break; #endif case S16_LE: { u32_t *optr = (u32_t *)(void *)outputptr; #if SL_LITTLE_ENDIAN if (gainL == FIXED_ONE && gainR == FIXED_ONE) { while (cnt--) { *(optr++) = (*(inputptr) >> 16 & 0x0000ffff) | (*(inputptr + 1) & 0xffff0000); inputptr += 2; } } else { while (cnt--) { *(optr++) = (gain(gainL, *(inputptr)) >> 16 & 0x0000ffff) | (gain(gainR, *(inputptr+1)) & 0xffff0000); inputptr += 2; } } #else if (gainL == FIXED_ONE && gainR == FIXED_ONE) { while (cnt--) { s32_t lsample = *(inputptr++); s32_t rsample = *(inputptr++); *(optr++) = (lsample & 0x00ff0000) << 8 | (lsample & 0xff000000) >> 8 | (rsample & 0x00ff0000) >> 8 | (rsample & 0xff000000) >> 24; } } else { while (cnt--) { s32_t lsample = gain(gainL, *(inputptr++)); s32_t rsample = gain(gainR, *(inputptr++)); *(optr++) = (lsample & 0x00ff0000) << 8 | (lsample & 0xff000000) >> 8 | (rsample & 0x00ff0000) >> 8 | (rsample & 0xff000000) >> 24; } } #endif } break; case S24_LE: { u32_t *optr = (u32_t *)(void *)outputptr; #if SL_LITTLE_ENDIAN if (gainL == FIXED_ONE && gainR == FIXED_ONE) { while (cnt--) { *(optr++) = *(inputptr++) >> 8; *(optr++) = *(inputptr++) >> 8; } } else { while (cnt--) { *(optr++) = gain(gainL, *(inputptr++)) >> 8; *(optr++) = gain(gainR, *(inputptr++)) >> 8; } } #else if (gainL == FIXED_ONE && gainR == FIXED_ONE) { while (cnt--) { s32_t lsample = *(inputptr++); s32_t rsample = *(inputptr++); *(optr++) = (lsample & 0xff000000) >> 16 | (lsample & 0x00ff0000) | (lsample & 0x0000ff00 << 16); *(optr++) = (rsample & 0xff000000) >> 16 | (rsample & 0x00ff0000) | (rsample & 0x0000ff00 << 16); } } else { while (cnt--) { s32_t lsample = gain(gainL, *(inputptr++)); s32_t rsample = gain(gainR, *(inputptr++)); *(optr++) = (lsample & 0xff000000) >> 16 | (lsample & 0x00ff0000) | (lsample & 0x0000ff00 << 16); *(optr++) = (rsample & 0xff000000) >> 16 | (rsample & 0x00ff0000) | (rsample & 0x0000ff00 << 16); } } #endif } break; case S24_3LE: { u8_t *optr = (u8_t *)(void *)outputptr; if (gainL == FIXED_ONE && gainR == FIXED_ONE) { while (cnt) { // attempt to do 32 bit memory accesses - move 2 frames at once: 16 bytes -> 12 bytes // falls through to exception case when not aligned or if less than 2 frames to move if (((uintptr_t)optr & 0x3) == 0 && cnt >= 2) { u32_t *o_ptr = (u32_t *)(void *)optr; while (cnt >= 2) { s32_t l1 = *(inputptr++); s32_t r1 = *(inputptr++); s32_t l2 = *(inputptr++); s32_t r2 = *(inputptr++); #if SL_LITTLE_ENDIAN *(o_ptr++) = (l1 & 0xffffff00) >> 8 | (r1 & 0x0000ff00) << 16; *(o_ptr++) = (r1 & 0xffff0000) >> 16 | (l2 & 0x00ffff00) << 8; *(o_ptr++) = (l2 & 0xff000000) >> 24 | (r2 & 0xffffff00); #else *(o_ptr++) = (l1 & 0x0000ff00) << 16 | (l1 & 0x00ff0000) | (l1 & 0xff000000) >> 16 | (r1 & 0x0000ff00) >> 8; *(o_ptr++) = (r1 & 0x00ff0000) << 8 | (r1 & 0xff000000) >> 8 | (l2 & 0x0000ff00) | (l2 & 0x00ff0000) >> 16; *(o_ptr++) = (l2 & 0xff000000) | (r2 & 0x0000ff00) << 8 | (r2 & 0x00ff0000) >> 8 | (r2 & 0xff000000) >> 24; #endif optr += 12; cnt -= 2; } } else { s32_t lsample = *(inputptr++); s32_t rsample = *(inputptr++); *(optr++) = (lsample & 0x0000ff00) >> 8; *(optr++) = (lsample & 0x00ff0000) >> 16; *(optr++) = (lsample & 0xff000000) >> 24; *(optr++) = (rsample & 0x0000ff00) >> 8; *(optr++) = (rsample & 0x00ff0000) >> 16; *(optr++) = (rsample & 0xff000000) >> 24; cnt--; } } } else { while (cnt) { // attempt to do 32 bit memory accesses - move 2 frames at once: 16 bytes -> 12 bytes // falls through to exception case when not aligned or if less than 2 frames to move if (((uintptr_t)optr & 0x3) == 0 && cnt >= 2) { u32_t *o_ptr = (u32_t *)(void *)optr; while (cnt >= 2) { s32_t l1 = gain(gainL, *(inputptr++)); s32_t r1 = gain(gainR, *(inputptr++)); s32_t l2 = gain(gainL, *(inputptr++)); s32_t r2 = gain(gainR, *(inputptr++)); #if SL_LITTLE_ENDIAN *(o_ptr++) = (l1 & 0xffffff00) >> 8 | (r1 & 0x0000ff00) << 16; *(o_ptr++) = (r1 & 0xffff0000) >> 16 | (l2 & 0x00ffff00) << 8; *(o_ptr++) = (l2 & 0xff000000) >> 24 | (r2 & 0xffffff00); #else *(o_ptr++) = (l1 & 0x0000ff00) << 16 | (l1 & 0x00ff0000) | (l1 & 0xff000000) >> 16 | (r1 & 0x0000ff00) >> 8; *(o_ptr++) = (r1 & 0x00ff0000) << 8 | (r1 & 0xff000000) >> 8 | (l2 & 0x0000ff00) | (l2 & 0x00ff0000) >> 16; *(o_ptr++) = (l2 & 0xff000000) | (r2 & 0x0000ff00) << 8 | (r2 & 0x00ff0000) >> 8 | (r2 & 0xff000000) >> 24; #endif optr += 12; cnt -= 2; } } else { s32_t lsample = gain(gainL, *(inputptr++)); s32_t rsample = gain(gainR, *(inputptr++)); *(optr++) = (lsample & 0x0000ff00) >> 8; *(optr++) = (lsample & 0x00ff0000) >> 16; *(optr++) = (lsample & 0xff000000) >> 24; *(optr++) = (rsample & 0x0000ff00) >> 8; *(optr++) = (rsample & 0x00ff0000) >> 16; *(optr++) = (rsample & 0xff000000) >> 24; cnt--; } } } } break; case S32_LE: { u32_t *optr = (u32_t *)(void *)outputptr; #if SL_LITTLE_ENDIAN if (gainL == FIXED_ONE && gainR == FIXED_ONE) { memcpy(outputptr, inputptr, cnt * BYTES_PER_FRAME); } else { while (cnt--) { *(optr++) = gain(gainL, *(inputptr++)); *(optr++) = gain(gainR, *(inputptr++)); } } #else if (gainL == FIXED_ONE && gainR == FIXED_ONE) { while (cnt--) { s32_t lsample = *(inputptr++); s32_t rsample = *(inputptr++); *(optr++) = (lsample & 0xff000000) >> 24 | (lsample & 0x00ff0000) >> 8 | (lsample & 0x0000ff00) << 8 | (lsample & 0x000000ff) << 24; *(optr++) = (rsample & 0xff000000) >> 24 | (rsample & 0x00ff0000) >> 8 | (rsample & 0x0000ff00) << 8 | (rsample & 0x000000ff) << 24; } } else { while (cnt--) { s32_t lsample = gain(gainL, *(inputptr++)); s32_t rsample = gain(gainR, *(inputptr++)); *(optr++) = (lsample & 0xff000000) >> 24 | (lsample & 0x00ff0000) >> 8 | (lsample & 0x0000ff00) << 8 | (lsample & 0x000000ff) << 24; *(optr++) = (rsample & 0xff000000) >> 24 | (rsample & 0x00ff0000) >> 8 | (rsample & 0x0000ff00) << 8 | (rsample & 0x000000ff) << 24; } } #endif } break; default: break; } } #if !WIN inline #endif void _apply_cross(struct buffer *outputbuf, frames_t out_frames, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) { s32_t *ptr = (s32_t *)(void *)outputbuf->readp; frames_t count = out_frames * 2; while (count--) { if (*cross_ptr > (s32_t *)outputbuf->wrap) { *cross_ptr -= outputbuf->size / BYTES_PER_FRAME * 2; } *ptr = gain(cross_gain_out, *ptr) + gain(cross_gain_in, **cross_ptr); ptr++; (*cross_ptr)++; } } #if !WIN inline #endif void _apply_gain(struct buffer *outputbuf, frames_t count, s32_t gainL, s32_t gainR, u8_t flags) { if (gainL == FIXED_ONE && gainR == FIXED_ONE && !(flags & (MONO_LEFT | MONO_RIGHT))) { return; } else if ((flags & MONO_LEFT) && (flags & MONO_RIGHT)) { ISAMPLE_T *ptrL = (ISAMPLE_T *)(void *)outputbuf->readp; ISAMPLE_T *ptrR = (ISAMPLE_T *)(void *)outputbuf->readp + 1; while (count--) { *ptrL = *ptrR = (gain(gainL, *ptrL) + gain(gainR, *ptrR)) / 2; ptrL += 2; ptrR += 2; } } else if (flags & MONO_RIGHT) { ISAMPLE_T *ptr = (ISAMPLE_T *)(void *)outputbuf->readp + 1; while (count--) { *(ptr - 1) = *ptr = gain(gainR, *ptr); ptr += 2; } } else if (flags & MONO_LEFT) { ISAMPLE_T *ptr = (ISAMPLE_T *)(void *)outputbuf->readp; while (count--) { *(ptr + 1) = *ptr = gain(gainL, *ptr); ptr += 2; } } else { ISAMPLE_T *ptrL = (ISAMPLE_T *)(void *)outputbuf->readp; ISAMPLE_T *ptrR = (ISAMPLE_T *)(void *)outputbuf->readp + 1; while (count--) { *ptrL = gain(gainL, *ptrL); *ptrR = gain(gainR, *ptrR); ptrL += 2; ptrR += 2; } } } squeezelite-1.9.9-1395+git20220104.874e4f9/output_pulse.c000066400000000000000000000360461416534420200220210ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additions (c) Paul Hermann, 2015-2021 under the same license terms * -Control of Raspberry pi GPIO for amplifier power * -Launch script on power status change from LMS */ // Output using PulseAudio #include "squeezelite.h" #if PULSEAUDIO #include #include // To report timing information back to the LMS the latency information needs to be // retrieved from the PulseAudio server. However this eats some CPU cycles and a few // bytes of RAM. Here you can decide if you want to retrieve precise timing information // or save a few CPU cycles. If you are running squeezelite on resource constrained // device (e.g. Raspberry Pi) and you want to keep the CPU temperature down then you // can set the PULSEAUDIO_TIMING to zero. In this case the sound card latency and // PulseAudio buffering won't be accounted for which might give you a slightly skewed // playback timing information. Otherwise keep this to default value of 2 and you // will get precise timing information. #ifndef PULSEAUDIO_TIMING # define PULSEAUDIO_TIMING 2 #endif typedef enum { readiness_unknown, readiness_ready, readiness_terminated, } pulse_readiness; typedef struct { pa_mainloop *loop; pa_context *ctx; pulse_readiness readiness; } pulse_connection; struct pulse { bool running; pa_stream *stream; pulse_readiness stream_readiness; pulse_connection conn; pa_sample_spec sample_spec; char *sink_name; }; static struct pulse pulse; static log_level loglevel; extern struct outputstate output; extern struct buffer *outputbuf; #define OUTPUT_STATE_TIMER_INTERVAL_USEC 100000 #define LOCK mutex_lock(outputbuf->mutex) #define UNLOCK mutex_unlock(outputbuf->mutex) extern u8_t *silencebuf; static void pulse_state_cb(pa_context *c, void *userdata) { pa_context_state_t state; pulse_connection *conn = userdata; state = pa_context_get_state(c); switch (state) { // There are just here for reference case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: default: break; case PA_CONTEXT_FAILED: case PA_CONTEXT_TERMINATED: conn->readiness = readiness_terminated; break; case PA_CONTEXT_READY: conn->readiness = readiness_ready; break; } } static inline bool pulse_connection_is_ready(pulse_connection *conn) { return conn->readiness == readiness_ready; } static inline bool pulse_connection_check_ready(pulse_connection *conn) { if (!pulse_connection_is_ready(conn)) { LOG_ERROR("connection to PulseAudio server has been terminated"); return false; } return true; } static inline void pulse_connection_iterate(pulse_connection *conn) { pa_mainloop_iterate(conn->loop, 1, NULL); } static inline pa_context * pulse_connection_get_context(pulse_connection *conn) { return conn->ctx; } static bool pulse_connection_init(pulse_connection *conn) { bool ret; conn->loop = pa_mainloop_new(); pa_mainloop_api *api = pa_mainloop_get_api(conn->loop); pa_proplist *proplist = pa_proplist_new(); pa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, VERSION); conn->ctx = pa_context_new_with_proplist(api, MODEL_NAME_STRING, proplist); pa_proplist_free(proplist); conn->readiness = readiness_unknown; bool connected = false; if (pa_context_connect(conn->ctx, (const char *)NULL, PA_CONTEXT_NOFLAGS, (const pa_spawn_api *)NULL) < 0) { LOG_ERROR("failed to connect to PulseAudio server: %s", pa_strerror(pa_context_errno(conn->ctx))); ret = false; } else { connected = true; pa_context_set_state_callback(conn->ctx, pulse_state_cb, conn); while (conn->readiness == readiness_unknown) { pa_mainloop_iterate(conn->loop, 1, NULL); } ret = pulse_connection_is_ready(conn); } if (!ret) { if (connected) pa_context_disconnect(conn->ctx); pa_context_unref(conn->ctx); pa_mainloop_free(conn->loop); } return ret; } static void pulse_connection_destroy(pulse_connection *conn) { pa_context_disconnect(conn->ctx); pa_context_unref(conn->ctx); pa_mainloop_free(conn->loop); } static bool pulse_operation_wait(pulse_connection *conn, pa_operation *op) { if (op == NULL) { LOG_ERROR("PulseAudio operation failed: %s", pa_strerror(pa_context_errno(conn->ctx))); return false; } pa_operation_state_t op_state; while (pulse_connection_check_ready(conn) && (op_state = pa_operation_get_state(op)) == PA_OPERATION_RUNNING) { pulse_connection_iterate(conn); } pa_operation_unref(op); if (!pulse_connection_is_ready(conn)) return false; return op_state == PA_OPERATION_DONE; } static void pulse_stream_state_cb(pa_stream *stream, void *userdata) { struct pulse *p = userdata; switch (pa_stream_get_state(stream)) { case PA_STREAM_UNCONNECTED: case PA_STREAM_CREATING: p->stream_readiness = readiness_unknown; break; case PA_STREAM_READY: p->stream_readiness = readiness_ready; break; case PA_STREAM_FAILED: case PA_STREAM_TERMINATED: p->stream_readiness = readiness_terminated; break; } } static void pulse_stream_success_noop_cb(pa_stream *s, int success, void *userdata) { } static bool pulse_stream_create(struct pulse *p) { p->sample_spec.rate = output.current_sample_rate; p->sample_spec.format = PA_SAMPLE_S32LE; // SqueezeLite internally always uses this format, let PulseAudio deal with eventual resampling. p->sample_spec.channels = 2; pa_proplist *proplist = pa_proplist_new(); pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "music"); pa_proplist_sets(proplist, PA_PROP_MEDIA_SOFTWARE, "Logitech Media Server"); p->stream = pa_stream_new_with_proplist(pulse_connection_get_context(&p->conn), "Logitech Media Server stream", &p->sample_spec, (const pa_channel_map *)NULL, proplist); pa_proplist_free(proplist); if (p->stream == NULL) return false; p->stream_readiness = readiness_unknown; pa_stream_set_state_callback(p->stream, pulse_stream_state_cb, p); if (pa_stream_connect_playback(p->stream, p->sink_name, (const pa_buffer_attr *)NULL, #if PULSEAUDIO_TIMING == 2 PA_STREAM_VARIABLE_RATE | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING, #else PA_STREAM_VARIABLE_RATE, #endif (const pa_cvolume *)NULL, (pa_stream *)NULL) < 0) { pa_stream_unref(p->stream); p->stream = NULL; return false; } bool ok; while ((ok = pulse_connection_check_ready(&p->conn) && p->running) && p->stream_readiness == readiness_unknown) { pulse_connection_iterate(&p->conn); } ok = ok && p->stream_readiness == readiness_ready; if (ok) { pa_buffer_attr attr = { 0, }; attr.maxlength = (uint32_t)(-1); attr.tlength = (uint32_t)(-1); attr.prebuf = (uint32_t)(-1); attr.minreq = (uint32_t)(-1); pa_operation *op = pa_stream_set_buffer_attr(p->stream, &attr, pulse_stream_success_noop_cb, NULL); ok = pulse_operation_wait(&p->conn, op); } if (!ok) { pa_stream_disconnect(p->stream); pa_stream_unref(p->stream); p->stream = NULL; } return ok; } static void pulse_stream_destroy(struct pulse *p) { if (p->stream) { pa_stream_disconnect(p->stream); pa_stream_unref(p->stream); p->stream = NULL; } } static void pulse_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { if (eol == 0) { printf(" %-50s %s\n", l->name, l->description); } else if (eol < 0) { LOG_WARN("error while listing PulseAudio sinks"); } } void list_devices(void) { pulse_connection conn; if (!pulse_connection_init(&conn)) return; int state = 0; pa_operation *op; while (pulse_connection_check_ready(&conn)) { if (state == 0) { printf("Output devices:\n"); op = pa_context_get_sink_info_list(pulse_connection_get_context(&conn), pulse_sinklist_cb, NULL); ++state; } else if (pa_operation_get_state(op) == PA_OPERATION_DONE) { pa_operation_unref(op); break; } pulse_connection_iterate(&conn); } pulse_connection_destroy(&conn); } static void pulse_set_volume(struct pulse *p, unsigned left, unsigned right) { uint32_t sink_input_idx = pa_stream_get_index(p->stream); pa_cvolume volume; pa_cvolume_init(&volume); volume.channels = 2; volume.values[0] = pa_sw_volume_from_dB(20 * log10(left / 65536.0)); volume.values[1] = left == right ? volume.values[0] : pa_sw_volume_from_dB(20 * log10(right / 65536.0)); pa_operation *op = pa_context_set_sink_input_volume(pulse_connection_get_context(&p->conn), sink_input_idx, &volume, NULL, NULL); if (op != NULL) { // This is send and forget operation, dereference it right away. if (loglevel >= lDEBUG) { char s[20]; LOG_DEBUG("sink input volume set to %s", pa_cvolume_snprint(s, sizeof(s), &volume)); } pa_operation_unref(op); } } void set_volume(unsigned left, unsigned right) { bool adjust_sink_input = false; LOCK; adjust_sink_input = (left != output.gainL) || (right != output.gainR); output.gainL = left; output.gainR = right; UNLOCK; if (adjust_sink_input && pulse.stream != NULL) { pulse_set_volume(&pulse, left, right); } } void set_sample_rate(uint32_t sample_rate) { pa_operation *op = pa_stream_update_sample_rate(pulse.stream, sample_rate, NULL, NULL); if (op != NULL) { if (loglevel >= lDEBUG) { LOG_DEBUG("stream sample rate set to %d Hz", sample_rate); } pa_operation_unref(op); } else { LOG_WARN("failed to set stream sample rate to %d Hz", sample_rate); } } struct test_open_data { unsigned *rates; bool userdef_rates; pa_sample_spec *sample_spec; bool is_default_device; char *default_sink_name; bool got_device; }; static void pulse_sinkinfo_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) { if (eol) return; struct test_open_data *d = userdata; d->got_device = true; if (d->is_default_device) d->default_sink_name = strdup(l->name); if (!d->userdef_rates) { d->rates[0] = PA_RATE_MAX; } output.default_sample_rate = l->sample_spec.rate; *d->sample_spec = l->sample_spec; } bool test_open(const char *device, unsigned rates[], bool userdef_rates) { struct test_open_data d = {0, }; d.rates = rates; d.userdef_rates = userdef_rates; d.sample_spec = &pulse.sample_spec; d.is_default_device = strcmp(device, "default") == 0; const char *sink_name = d.is_default_device ? NULL : device; pa_operation *op = pa_context_get_sink_info_by_name(pulse_connection_get_context(&pulse.conn), sink_name, pulse_sinkinfo_cb, &d); if (!pulse_operation_wait(&pulse.conn, op)) return false; if (!d.got_device) return false; pulse.sink_name = d.is_default_device ? d.default_sink_name : (char *)device; return true; } static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) { pa_stream_write(pulse.stream, silence ? silencebuf : outputbuf->readp, out_frames * BYTES_PER_FRAME, (pa_free_cb_t)NULL, 0, PA_SEEK_RELATIVE); return (int)out_frames; } void output_state_timer_cb(pa_mainloop_api *api, pa_time_event *e, const struct timeval *tv_, void *userdata) { struct pulse *p = userdata; pa_context_rttime_restart(pulse_connection_get_context(&p->conn), e, pa_rtclock_now() + OUTPUT_STATE_TIMER_INTERVAL_USEC); } #if PULSEAUDIO_TIMING == 0 # define DECLARE_LATENCY(n) (void)0 # define pulse_retrieve_latency(n) true # define pulse_get_latency(n) 0 #elif PULSEAUDIO_TIMING == 2 # define DECLARE_LATENCY(n) pa_usec_t n static inline bool pulse_retrieve_latency(pa_usec_t *usec) { return pa_stream_get_latency(pulse.stream, usec, NULL) == 0; } #endif #if PULSEAUDIO_TIMING > 0 static inline unsigned pulse_get_latency(pa_usec_t usec) { return (unsigned)((usec * output.current_sample_rate) / PA_USEC_PER_SEC); } #endif static void * output_thread(void *arg) { bool output_off = (output.state == OUTPUT_OFF); pa_time_event *output_state_timer = NULL; while (pulse.running) { if (output_off) { if (pulse.stream != NULL) { LOG_DEBUG("destroying PulseAudio playback stream"); pulse_stream_destroy(&pulse); } if (output_state_timer == NULL) { output_state_timer = pa_context_rttime_new(pulse_connection_get_context(&pulse.conn), pa_rtclock_now() + OUTPUT_STATE_TIMER_INTERVAL_USEC, output_state_timer_cb, &pulse); } } else { if (output_state_timer != NULL) { pa_mainloop_api *api = pa_mainloop_get_api(pulse.conn.loop); api->time_free(output_state_timer); output_state_timer = NULL; } if (pulse.stream == NULL) { if (pulse_stream_create(&pulse)) { LOG_DEBUG("PulseAudio playback stream on sink %s open", pulse.sink_name); unsigned left, right; LOCK; left = output.gainL; right = output.gainR; UNLOCK; pulse_set_volume(&pulse, left, right); } else { if (!pulse.running) break; output.error_opening = true; } } if (pulse.stream != NULL) { size_t writable = pa_stream_writable_size(pulse.stream); if (writable > 0) { DECLARE_LATENCY(latency); bool latency_ok = pulse_retrieve_latency(&latency); frames_t frame_count = writable / pa_sample_size(pa_stream_get_sample_spec(pulse.stream)); LOCK; if (latency_ok) { output.device_frames = pulse_get_latency(latency); output.updated = gettime_ms(); output.frames_played_dmp = output.frames_played; } _output_frames(frame_count); UNLOCK; } } } pulse_connection_iterate(&pulse.conn); output_off = (output.state == OUTPUT_OFF); } pulse_stream_destroy(&pulse); return NULL; } static pthread_t thread; void output_init_pulse(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle) { loglevel = level; LOG_INFO("init output"); output.format = 0; output.start_frames = 0; output.write_cb = &_write_frames; output.rate_delay = rate_delay; if (!pulse_connection_init(&pulse.conn)) { // In case of an error, the message is logged by the pulse_connection_init itself. exit(1); } output_init_common(level, device, output_buf_size, rates, idle); // start output thread pulse.running = true; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE); pthread_create(&thread, &attr, output_thread, NULL); pthread_attr_destroy(&attr); } void output_close_pulse(void) { LOG_INFO("close output"); pulse.running = false; pa_mainloop_wakeup(pulse.conn.loop); pthread_join(thread, NULL); if (output.device != pulse.sink_name) free(pulse.sink_name); pulse_connection_destroy(&pulse.conn); output_close_common(); } #endif squeezelite-1.9.9-1395+git20220104.874e4f9/output_stdout.c000066400000000000000000000102551416534420200222050ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // Stdout output #include "squeezelite.h" #define FRAME_BLOCK MAX_SILENCE_FRAMES static log_level loglevel; static bool running = true; extern struct outputstate output; extern struct buffer *outputbuf; #define LOCK mutex_lock(outputbuf->mutex) #define UNLOCK mutex_unlock(outputbuf->mutex) extern u8_t *silencebuf; #if DSD extern u8_t *silencebuf_dsd; #endif // buffer to hold output data so we can block on writing outside of output lock, allocated on init static u8_t *buf; static unsigned buffill; static int bytes_per_frame; static int _stdout_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) { u8_t *obuf; if (!silence) { if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) { _apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr); } obuf = outputbuf->readp; } else { obuf = silencebuf; } IF_DSD( if (output.outfmt != PCM) { if (silence) { obuf = silencebuf_dsd; } if (output.outfmt == DOP) update_dop((u32_t *)obuf, out_frames, output.invert && !silence); else if (output.invert && !silence) dsd_invert((u32_t *)obuf, out_frames); } ) _scale_and_pack_frames(buf + buffill * bytes_per_frame, (s32_t *)(void *)obuf, out_frames, gainL, gainR, flags, output.format); buffill += out_frames; return (int)out_frames; } static void *output_thread() { LOCK; switch (output.format) { case S32_LE: bytes_per_frame = 4 * 2; break; case S24_3LE: bytes_per_frame = 3 * 2; break; case S16_LE: bytes_per_frame = 2 * 2; break; default: bytes_per_frame = 4 * 2; break; break; } UNLOCK; while (running) { LOCK; output.device_frames = 0; output.updated = gettime_ms(); output.frames_played_dmp = output.frames_played; _output_frames(FRAME_BLOCK); UNLOCK; if (buffill) { fwrite(buf, bytes_per_frame, buffill, stdout); buffill = 0; } } return 0; } static thread_type thread; void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay) { loglevel = level; LOG_INFO("init output stdout"); buf = malloc(FRAME_BLOCK * BYTES_PER_FRAME); if (!buf) { LOG_ERROR("unable to malloc buf"); return; } buffill = 0; memset(&output, 0, sizeof(output)); output.format = S32_LE; output.start_frames = FRAME_BLOCK * 2; output.write_cb = &_stdout_write_frames; output.rate_delay = rate_delay; if (params) { if (!strcmp(params, "32")) output.format = S32_LE; if (!strcmp(params, "24")) output.format = S24_3LE; if (!strcmp(params, "16")) output.format = S16_LE; } // ensure output rate is specified to avoid test open if (!rates[0]) { rates[0] = 44100; } output_init_common(level, "-", output_buf_size, rates, 0); #if LINUX || OSX || FREEBSD pthread_attr_t attr; pthread_attr_init(&attr); #ifdef PTHREAD_STACK_MIN pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE); #endif pthread_create(&thread, &attr, output_thread, NULL); pthread_attr_destroy(&attr); #endif #if WIN thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&output_thread, NULL, 0, NULL); #endif } void output_close_stdout(void) { LOG_INFO("close output"); LOCK; running = false; UNLOCK; free(buf); output_close_common(); } squeezelite-1.9.9-1395+git20220104.874e4f9/output_vis.c000066400000000000000000000113051416534420200214610ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // Export audio samples for visualizer process (16 bit only best endevours) #include "squeezelite.h" #if VISEXPORT #include #include #include #if OSX #include #include static int pthread_rwlock_timedwrlock( pthread_rwlock_t * restrict rwlock, const struct timespec * restrict abs_timeout ) { ( void )rwlock; ( void )abs_timeout; return 0; } #endif #define VIS_BUF_SIZE 16384 #define VIS_LOCK_NS 1000000 // ns to wait for vis wrlock static struct vis_t { pthread_rwlock_t rwlock; u32_t buf_size; u32_t buf_index; bool running; u32_t rate; time_t updated; s16_t buffer[VIS_BUF_SIZE]; } *vis_mmap = NULL; static char vis_shm_path[40]; static int vis_fd = -1; static log_level loglevel; // attempt to write audio to vis_mmap but do not wait more than VIS_LOCK_NS to get wrlock // this can result in missing audio export to the mmap region, but this is preferable dropping audio void _vis_export(struct buffer *outputbuf, struct outputstate *output, frames_t out_frames, bool silence) { if (vis_mmap) { int err; err = pthread_rwlock_trywrlock(&vis_mmap->rwlock); if (err) { struct timespec ts; #if OSX clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); ts.tv_sec = mts.tv_sec; ts.tv_nsec = mts.tv_nsec; #else clock_gettime(CLOCK_REALTIME, &ts); #endif ts.tv_nsec += VIS_LOCK_NS; if (ts.tv_nsec > 1000000000) { ts.tv_sec += 1; ts.tv_nsec -= 1000000000; } err = pthread_rwlock_timedwrlock(&vis_mmap->rwlock, &ts); } if (err) { LOG_DEBUG("failed to get wrlock - skipping visulizer export"); } else { if (silence) { vis_mmap->running = false; } else { frames_t vis_cnt = out_frames; s32_t *ptr = (s32_t *) outputbuf->readp; unsigned i = vis_mmap->buf_index; if (!output->current_replay_gain) { while (vis_cnt--) { vis_mmap->buffer[i++] = *(ptr++) >> 16; vis_mmap->buffer[i++] = *(ptr++) >> 16; if (i == VIS_BUF_SIZE) i = 0; } } else { while (vis_cnt--) { vis_mmap->buffer[i++] = gain(*(ptr++), output->current_replay_gain) >> 16; vis_mmap->buffer[i++] = gain(*(ptr++), output->current_replay_gain) >> 16; if (i == VIS_BUF_SIZE) i = 0; } } vis_mmap->updated = time(NULL); vis_mmap->running = true; vis_mmap->buf_index = i; vis_mmap->rate = output->current_sample_rate; } pthread_rwlock_unlock(&vis_mmap->rwlock); } } } void vis_stop(void) { if (vis_mmap) { pthread_rwlock_wrlock(&vis_mmap->rwlock); vis_mmap->running = false; pthread_rwlock_unlock(&vis_mmap->rwlock); } } void output_vis_init(log_level level, u8_t *mac) { loglevel = level; sprintf(vis_shm_path, "/squeezelite-%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mode_t old_mask = umask(000); // allow any user to read our shm when created #if OSX /* ftruncate on MacOS only works on the initial segment creation */ shm_unlink(vis_shm_path); #endif vis_fd = shm_open(vis_shm_path, O_CREAT | O_RDWR, 0666); if (vis_fd != -1) { if (ftruncate(vis_fd, sizeof(struct vis_t)) == 0) { vis_mmap = (struct vis_t *)mmap(NULL, sizeof(struct vis_t), PROT_READ | PROT_WRITE, MAP_SHARED, vis_fd, 0); } } if (vis_mmap > 0) { pthread_rwlockattr_t attr; pthread_rwlockattr_init(&attr); pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); pthread_rwlock_init(&vis_mmap->rwlock, &attr); vis_mmap->buf_size = VIS_BUF_SIZE; vis_mmap->running = false; vis_mmap->rate = 44100; pthread_rwlockattr_destroy(&attr); LOG_INFO("opened visulizer shared memory as %s", vis_shm_path); } else { LOG_WARN("unable to open visualizer shared memory"); vis_mmap = NULL; } umask(old_mask); } #endif // VISEXPORT squeezelite-1.9.9-1395+git20220104.874e4f9/pcm.c000066400000000000000000000316031416534420200200220ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #if BYTES_PER_FRAME == 4 #define SHIFT 16 #define OPTR_T u16_t #else #define OPTR_T u32_t #define SHIFT 0 #endif extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; bool pcm_check_header = false; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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 #define MAX_DECODE_FRAMES 4096 static u32_t sample_rates[] = { 11025, 22050, 32000, 44100, 48000, 8000, 12000, 16000, 24000, 96000, 88200, 176400, 192000, 352800, 384000, 705600, 768000, 1411200, 1536000 }; static u32_t sample_rate; static u32_t sample_size; static u32_t channels; static bool bigendian; static bool limit; static u32_t audio_left; static u32_t bytes_per_frame; typedef enum { UNKNOWN = 0, WAVE, AIFF } header_format; static void _check_header(void) { u8_t *ptr = streambuf->readp; unsigned bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); header_format format = UNKNOWN; // simple parsing of wav and aiff headers and get to samples if (bytes > 12) { if (!memcmp(ptr, "RIFF", 4) && !memcmp(ptr+8, "WAVE", 4)) { LOG_INFO("WAVE"); format = WAVE; } else if (!memcmp(ptr, "FORM", 4) && (!memcmp(ptr+8, "AIFF", 4) || !memcmp(ptr+8, "AIFC", 4))) { LOG_INFO("AIFF"); format = AIFF; } } if (format != UNKNOWN) { ptr += 12; bytes -= 12; while (bytes >= 8) { char id[5]; unsigned len; memcpy(id, ptr, 4); id[4] = '\0'; if (format == WAVE) { len = *(ptr+4) | *(ptr+5) << 8 | *(ptr+6) << 16| *(ptr+7) << 24; } else { len = *(ptr+4) << 24 | *(ptr+5) << 16 | *(ptr+6) << 8 | *(ptr+7); } LOG_INFO("header: %s len: %d", id, len); if (format == WAVE && !memcmp(ptr, "data", 4)) { ptr += 8; _buf_inc_readp(streambuf, ptr - streambuf->readp); audio_left = len; if ((audio_left == 0xFFFFFFFF) || (audio_left == 0x7FFFEFFC)) { LOG_INFO("wav audio size unknown: %u", audio_left); limit = false; } else { LOG_INFO("wav audio size: %u", audio_left); limit = true; } return; } if (format == AIFF && !memcmp(ptr, "SSND", 4) && bytes >= 16) { unsigned offset = *(ptr+8) << 24 | *(ptr+9) << 16 | *(ptr+10) << 8 | *(ptr+11); // following 4 bytes is blocksize - ignored ptr += 8 + 8; _buf_inc_readp(streambuf, ptr + offset - streambuf->readp); // Reading from an upsampled stream, length could be wrong. // Only use length in header for files. if (stream.state == STREAMING_FILE) { audio_left = len - 8 - offset; LOG_INFO("aif audio size: %u", audio_left); limit = true; } return; } if (format == WAVE && !memcmp(ptr, "fmt ", 4) && bytes >= 24) { // override the server parsed values with our own channels = *(ptr+10) | *(ptr+11) << 8; sample_rate = *(ptr+12) | *(ptr+13) << 8 | *(ptr+14) << 16 | *(ptr+15) << 24; sample_size = (*(ptr+22) | *(ptr+23) << 8) / 8; bigendian = 0; LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian); } if (format == AIFF && !memcmp(ptr, "COMM", 4) && bytes >= 26) { int exponent; // override the server parsed values with our own channels = *(ptr+8) << 8 | *(ptr+9); sample_size = (*(ptr+14) << 8 | *(ptr+15)) / 8; bigendian = 1; // sample rate is encoded as IEEE 80 bit extended format // make some assumptions to simplify processing - only use first 32 bits of mantissa exponent = ((*(ptr+16) & 0x7f) << 8 | *(ptr+17)) - 16383 - 31; sample_rate = *(ptr+18) << 24 | *(ptr+19) << 16 | *(ptr+20) << 8 | *(ptr+21); while (exponent < 0) { sample_rate >>= 1; ++exponent; } while (exponent > 0) { sample_rate <<= 1; --exponent; } LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian); } if (bytes >= len + 8) { ptr += len + 8; bytes -= (len + 8); } else { LOG_WARN("run out of data"); return; } } } else { LOG_WARN("unknown format - can't parse header"); } } static decode_state pcm_decode(void) { unsigned bytes, in, out; frames_t frames, count; OPTR_T *optr; u8_t *iptr; u8_t tmp[3*8]; LOCK_S; if ( decode.new_stream && ( ( stream.state == STREAMING_FILE ) || pcm_check_header ) ) { _check_header(); } LOCK_O_direct; bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); IF_DIRECT( out = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; ); IF_PROCESS( out = process.max_in_frames; ); if ((stream.state <= DISCONNECT && bytes < bytes_per_frame) || (limit && audio_left == 0)) { UNLOCK_O_direct; UNLOCK_S; return DECODE_COMPLETE; } if (decode.new_stream) { LOG_INFO("setting track_start"); LOCK_O_not_direct; output.track_start = outputbuf->writep; decode.new_stream = false; #if DSD if (sample_size == 3 && is_stream_dop(((u8_t *)streambuf->readp) + (bigendian?0:2), ((u8_t *)streambuf->readp) + (bigendian?0:2) + sample_size, sample_size * channels, bytes / (sample_size * channels))) { LOG_INFO("file contains DOP"); if (output.dsdfmt == DOP_S24_LE || output.dsdfmt == DOP_S24_3LE) output.next_fmt = output.dsdfmt; else output.next_fmt = DOP; output.next_sample_rate = sample_rate; output.fade = FADE_INACTIVE; } else { output.next_sample_rate = decode_newstream(sample_rate, output.supported_rates); output.next_fmt = PCM; if (output.fade_mode) _checkfade(true); } #else output.next_sample_rate = decode_newstream(sample_rate, output.supported_rates); if (output.fade_mode) _checkfade(true); #endif UNLOCK_O_not_direct; IF_PROCESS( out = process.max_in_frames; ); bytes_per_frame = channels * sample_size; } IF_DIRECT( optr = (OPTR_T *)outputbuf->writep; ); IF_PROCESS( optr = (OPTR_T *)process.inbuf; ); iptr = (u8_t *)streambuf->readp; in = bytes / bytes_per_frame; // handle frame wrapping round end of streambuf // - only need if resizing of streambuf does not avoid this, could occur in localfile case if (in == 0 && bytes > 0 && _buf_used(streambuf) >= bytes_per_frame) { memcpy(tmp, iptr, bytes); memcpy(tmp + bytes, streambuf->buf, bytes_per_frame - bytes); iptr = tmp; in = 1; } frames = min(in, out); frames = min(frames, MAX_DECODE_FRAMES); if (limit && frames * bytes_per_frame > audio_left) { LOG_INFO("reached end of audio"); frames = audio_left / bytes_per_frame; } count = frames * channels; if (channels == 2) { if (sample_size == 1) { while (count--) { *optr++ = *iptr++ << (24-SHIFT); } } else if (sample_size == 2) { if (bigendian) { #if BYTES_PER_FRAME == 4 && !SL_LITTLE_ENDIAN // while loop below works as is, but memcpy is a win for that 16/16 typical case memcpy(optr, iptr, count * BYTES_PER_FRAME / 2); #else while (count--) { *optr++ = *(iptr) << (24-SHIFT) | *(iptr+1) << (16-SHIFT); iptr += 2; } #endif } else { #if BYTES_PER_FRAME == 4 && SL_LITTLE_ENDIAN // while loop below works as is, but memcpy is a win for that 16/16 typical case memcpy(optr, iptr, count * BYTES_PER_FRAME / 2); #else while (count--) { *optr++ = *(iptr) << (16-SHIFT) | *(iptr+1) << (24-SHIFT); iptr += 2; } #endif } } else if (sample_size == 3) { if (bigendian) { while (count--) { #if BYTES_PER_FRAME == 4 *optr++ = *(iptr) << 8 | *(iptr+1); #else *optr++ = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8; #endif iptr += 3; } } else { while (count--) { #if BYTES_PER_FRAME == 4 *optr++ = *(iptr+1) | *(iptr+2) << 8; #else *optr++ = *(iptr) << 8 | *(iptr+1) << 16 | *(iptr+2) << 24; #endif iptr += 3; } } } else if (sample_size == 4) { if (bigendian) { while (count--) { #if BYTES_PER_FRAME == 4 *optr++ = *(iptr) << 8 | *(iptr+1); #else *optr++ = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3); #endif iptr += 4; } } else { while (count--) { #if BYTES_PER_FRAME == 4 *optr++ = *(iptr+2) | *(iptr+3) << 8; #else *optr++ = *(iptr) | *(iptr+1) << 8 | *(iptr+2) << 16 | *(iptr+3) << 24; #endif iptr += 4; } } } } else if (channels == 1) { if (sample_size == 1) { while (count--) { *optr = *iptr++ << (24-SHIFT); *(optr+1) = *optr; optr += 2; } } else if (sample_size == 2) { if (bigendian) { while (count--) { *optr = *(iptr) << (24-SHIFT) | *(iptr+1) << (16-SHIFT); *(optr+1) = *optr; iptr += 2; optr += 2; } } else { while (count--) { *optr = *(iptr) << (16-SHIFT) | *(iptr+1) << (24-SHIFT); *(optr+1) = *optr; iptr += 2; optr += 2; } } } else if (sample_size == 3) { if (bigendian) { while (count--) { #if BYTES_PER_FRAME == 4 *optr++ = *(iptr) << 8 | *(iptr+1); #else *optr = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8; #endif *(optr+1) = *optr; iptr += 3; optr += 2; } } else { while (count--) { #if BYTES_PER_FRAME == 4 *optr++ = *(iptr+1) | *(iptr+2) << 8; #else *optr = *(iptr) << 8 | *(iptr+1) << 16 | *(iptr+2) << 24; #endif *(optr+1) = *optr; iptr += 3; optr += 2; } } } else if (sample_size == 4) { if (bigendian) { while (count--) { #if BYTES_PER_FRAME == 4 *optr++ = *(iptr) << 8 | *(iptr+1); #else *optr++ = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3); #endif *(optr+1) = *optr; iptr += 4; optr += 2; } } else { while (count--) { #if BYTES_PER_FRAME == 4 *optr++ = *(iptr+2) | *(iptr+3) << 8; #else *optr++ = *(iptr) | *(iptr+1) << 8 | *(iptr+2) << 16 | *(iptr+3) << 24; #endif *(optr+1) = *optr; iptr += 4; optr += 2; } } } } else { LOG_ERROR("unsupported channels"); } LOG_SDEBUG("decoded %u frames", frames); _buf_inc_readp(streambuf, frames * bytes_per_frame); if (limit) { audio_left -= frames * bytes_per_frame; } IF_DIRECT( _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames = frames; ); UNLOCK_O_direct; UNLOCK_S; return DECODE_RUNNING; } static void pcm_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { sample_size = size - '0' + 1; sample_rate = sample_rates[rate - '0']; channels = chan - '0'; bigendian = (endianness == '0'); limit = false; LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian); buf_adjust(streambuf, sample_size * channels); } static void pcm_close(void) { buf_adjust(streambuf, 1); } struct codec *register_pcm(void) { if ( pcm_check_header ) { static struct codec ret = { 'p', // id "wav,aif,pcm", // types 4096, // min read 102400, // min space pcm_open, // open pcm_close, // close pcm_decode, // decode }; LOG_INFO("using pcm to decode wav,aif,pcm"); return &ret; } else { static struct codec ret = { 'p', // id "aif,pcm", // types 4096, // min read 102400, // min space pcm_open, // open pcm_close, // close pcm_decode, // decode }; LOG_INFO("using pcm to decode aif,pcm"); return &ret; } return NULL; } squeezelite-1.9.9-1395+git20220104.874e4f9/process.c000066400000000000000000000116171416534420200207240ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // sample processing - only included when building with PROCESS set #include "squeezelite.h" #if PROCESS extern log_level loglevel; extern struct buffer *outputbuf; extern struct decodestate decode; struct processstate process; extern struct codec *codec; #define LOCK_D mutex_lock(decode.mutex); #define UNLOCK_D mutex_unlock(decode.mutex); #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) // macros to map to processing functions - currently only resample.c // this can be made more generic when multiple processing mechanisms get added #if RESAMPLE #define SAMPLES_FUNC resample_samples #define DRAIN_FUNC resample_drain #define NEWSTREAM_FUNC resample_newstream #define FLUSH_FUNC resample_flush #define INIT_FUNC resample_init #endif // transfer all processed frames to the output buf static void _write_samples(void) { frames_t frames = process.out_frames; u32_t *iptr = (u32_t *)process.outbuf; unsigned cnt = 10; LOCK_O; while (frames > 0) { frames_t f = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; u32_t *optr = (u32_t *)outputbuf->writep; if (f > 0) { f = min(f, frames); memcpy(optr, iptr, f * BYTES_PER_FRAME); frames -= f; _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); iptr += f * BYTES_PER_FRAME / sizeof(*iptr); } else if (cnt--) { // there should normally be space in the output buffer, but may need to wait during drain phase UNLOCK_O; usleep(10000); LOCK_O; } else { // bail out if no space found after 100ms to avoid locking LOG_ERROR("unable to get space in output buffer"); UNLOCK_O; return; } } UNLOCK_O; } // process samples - called with decode mutex set void process_samples(void) { SAMPLES_FUNC(&process); _write_samples(); process.in_frames = 0; } // drain at end of track - called with decode mutex set void process_drain(void) { bool done; do { done = DRAIN_FUNC(&process); _write_samples(); } while (!done); LOG_DEBUG("processing track complete - frames in: %lu out: %lu", process.total_in, process.total_out); } // new stream - called with decode mutex set unsigned process_newstream(bool *direct, unsigned raw_sample_rate, unsigned supported_rates[]) { bool active = NEWSTREAM_FUNC(&process, raw_sample_rate, supported_rates); LOG_INFO("processing: %s", active ? "active" : "inactive"); *direct = !active; if (active) { unsigned max_in_frames, max_out_frames; process.in_frames = process.out_frames = 0; process.total_in = process.total_out = 0; max_in_frames = codec->min_space / BYTES_PER_FRAME ; // increase size of output buffer by 10% as output rate is not an exact multiple of input rate if (process.out_sample_rate % process.in_sample_rate == 0) { max_out_frames = max_in_frames * (process.out_sample_rate / process.in_sample_rate); } else { max_out_frames = (int)(1.1 * (float)max_in_frames * (float)process.out_sample_rate / (float)process.in_sample_rate); } if (process.max_in_frames != max_in_frames) { LOG_DEBUG("creating process buf in frames: %u", max_in_frames); if (process.inbuf) free(process.inbuf); process.inbuf = malloc(max_in_frames * BYTES_PER_FRAME); process.max_in_frames = max_in_frames; } if (process.max_out_frames != max_out_frames) { LOG_DEBUG("creating process buf out frames: %u", max_out_frames); if (process.outbuf) free(process.outbuf); process.outbuf = malloc(max_out_frames * BYTES_PER_FRAME); process.max_out_frames = max_out_frames; } if (!process.inbuf || !process.outbuf) { LOG_ERROR("malloc fail creating process buffers"); *direct = true; return raw_sample_rate; } return process.out_sample_rate; } return raw_sample_rate; } // process flush - called with decode mutex set void process_flush(void) { LOG_INFO("process flush"); FLUSH_FUNC(); process.in_frames = 0; } // init - called with no mutex void process_init(char *opt) { bool enabled = INIT_FUNC(opt); memset(&process, 0, sizeof(process)); if (enabled) { LOCK_D; decode.process = true; UNLOCK_D; } } #endif // #if PROCESS squeezelite-1.9.9-1395+git20220104.874e4f9/resample.c000066400000000000000000000237211416534420200210550ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // upsampling using libsoxr - only included if RESAMPLE set #include "squeezelite.h" #if RESAMPLE #include #include extern log_level loglevel; struct soxr { soxr_t resampler; size_t old_clips; unsigned long q_recipe; unsigned long q_flags; double q_precision; /* Conversion precision (in bits). 20 */ double q_phase_response; /* 0=minimum, ... 50=linear, ... 100=maximum 50 */ double q_passband_end; /* 0dB pt. bandwidth to preserve; nyquist=1 0.913 */ double q_stopband_begin; /* Aliasing/imaging control; > passband_end 1 */ double scale; bool max_rate; bool exception; #if !LINKALL // soxr symbols to be dynamically loaded soxr_io_spec_t (* soxr_io_spec)(soxr_datatype_t itype, soxr_datatype_t otype); soxr_quality_spec_t (* soxr_quality_spec)(unsigned long recipe, unsigned long flags); soxr_t (* soxr_create)(double, double, unsigned, soxr_error_t *, soxr_io_spec_t const *, soxr_quality_spec_t const *, soxr_runtime_spec_t const *); void (* soxr_delete)(soxr_t); soxr_error_t (* soxr_process)(soxr_t, soxr_in_t, size_t, size_t *, soxr_out_t, size_t olen, size_t *); size_t *(* soxr_num_clips)(soxr_t); #if RESAMPLE_MP soxr_runtime_spec_t (* soxr_runtime_spec)(unsigned num_threads); #endif // soxr_strerror is a macro so not included here #endif }; static struct soxr *r; #if LINKALL #define SOXR(h, fn, ...) (soxr_ ## fn)(__VA_ARGS__) #else #define SOXR(h, fn, ...) (h)->soxr_##fn(__VA_ARGS__) #endif void resample_samples(struct processstate *process) { size_t idone, odone; size_t clip_cnt; soxr_error_t error = SOXR(r, process, r->resampler, process->inbuf, process->in_frames, &idone, process->outbuf, process->max_out_frames, &odone); if (error) { LOG_INFO("soxr_process error: %s", soxr_strerror(error)); return; } if (idone != process->in_frames) { // should not get here if buffers are big enough... LOG_ERROR("should not get here - partial sox process: %u of %u processed %u of %u out", (unsigned)idone, process->in_frames, (unsigned)odone, process->max_out_frames); } process->out_frames = odone; process->total_in += idone; process->total_out += odone; clip_cnt = *(SOXR(r, num_clips, r->resampler)); if (clip_cnt - r->old_clips) { LOG_SDEBUG("resampling clips: %u", (unsigned)(clip_cnt - r->old_clips)); r->old_clips = clip_cnt; } } bool resample_drain(struct processstate *process) { size_t odone; size_t clip_cnt; soxr_error_t error = SOXR(r, process, r->resampler, NULL, 0, NULL, process->outbuf, process->max_out_frames, &odone); if (error) { LOG_INFO("soxr_process error: %s", soxr_strerror(error)); return true; } process->out_frames = odone; process->total_out += odone; clip_cnt = *(SOXR(r, num_clips, r->resampler)); if (clip_cnt - r->old_clips) { LOG_DEBUG("resampling clips: %u", (unsigned)(clip_cnt - r->old_clips)); r->old_clips = clip_cnt; } if (odone == 0) { LOG_INFO("resample track complete - total track clips: %u", r->old_clips); SOXR(r, delete, r->resampler); r->resampler = NULL; return true; } else { return false; } } bool resample_newstream(struct processstate *process, unsigned raw_sample_rate, unsigned supported_rates[]) { unsigned outrate = 0; int i; if (r->exception) { // find direct match - avoid resampling for (i = 0; supported_rates[i]; i++) { if (raw_sample_rate == supported_rates[i]) { outrate = raw_sample_rate; break; } } // else find next highest sync sample rate while (!outrate && i >= 0) { if (supported_rates[i] > raw_sample_rate && supported_rates[i] % raw_sample_rate == 0) { outrate = supported_rates[i]; break; } i--; } } if (!outrate) { if (r->max_rate) { // resample to max rate for device outrate = supported_rates[0]; } else { // resample to max sync sample rate for (i = 0; supported_rates[i]; i++) { if (supported_rates[i] % raw_sample_rate == 0 || raw_sample_rate % supported_rates[i] == 0) { outrate = supported_rates[i]; break; } } } if (!outrate) { outrate = supported_rates[0]; } } process->in_sample_rate = raw_sample_rate; process->out_sample_rate = outrate; if (r->resampler) { SOXR(r, delete, r->resampler); r->resampler = NULL; } if (raw_sample_rate != outrate) { soxr_io_spec_t io_spec; soxr_quality_spec_t q_spec; soxr_error_t error; #if RESAMPLE_MP soxr_runtime_spec_t r_spec; #endif LOG_INFO("resampling from %u -> %u", raw_sample_rate, outrate); io_spec = SOXR(r, io_spec, SOXR_INT32_I, SOXR_INT32_I); io_spec.scale = r->scale; q_spec = SOXR(r, quality_spec, r->q_recipe, r->q_flags); if (r->q_precision > 0) { q_spec.precision = r->q_precision; } if (r->q_passband_end > 0) { q_spec.passband_end = r->q_passband_end; } if (r->q_stopband_begin > 0) { q_spec.stopband_begin = r->q_stopband_begin; } if (r->q_phase_response > -1) { q_spec.phase_response = r->q_phase_response; } #if RESAMPLE_MP r_spec = SOXR(r, runtime_spec, 0); // make use of libsoxr OpenMP support allowing parallel execution if multiple cores #endif LOG_DEBUG("resampling with soxr_quality_spec_t[precision: %03.1f, passband_end: %03.6f, stopband_begin: %03.6f, " "phase_response: %03.1f, flags: 0x%02x], soxr_io_spec_t[scale: %03.2f]", q_spec.precision, q_spec.passband_end, q_spec.stopband_begin, q_spec.phase_response, q_spec.flags, io_spec.scale); #if RESAMPLE_MP r->resampler = SOXR(r, create, raw_sample_rate, outrate, 2, &error, &io_spec, &q_spec, &r_spec); #else r->resampler = SOXR(r, create, raw_sample_rate, outrate, 2, &error, &io_spec, &q_spec, NULL); #endif if (error) { LOG_INFO("soxr_create error: %s", soxr_strerror(error)); return false; } r->old_clips = 0; return true; } else { LOG_INFO("disable resampling - rates match"); return false; } } void resample_flush(void) { if (r->resampler) { SOXR(r, delete, r->resampler); r->resampler = NULL; } } static bool load_soxr(void) { #if !LINKALL void *handle = dlopen(LIBSOXR, RTLD_NOW); char *err; if (!handle) { LOG_INFO("dlerror: %s", dlerror()); return false; } r->soxr_io_spec = dlsym(handle, "soxr_io_spec"); r->soxr_quality_spec = dlsym(handle, "soxr_quality_spec"); r->soxr_create = dlsym(handle, "soxr_create"); r->soxr_delete = dlsym(handle, "soxr_delete"); r->soxr_process = dlsym(handle, "soxr_process"); r->soxr_num_clips = dlsym(handle, "soxr_num_clips"); #if RESAMPLE_MP r->soxr_runtime_spec = dlsym(handle, "soxr_runtime_spec"); #endif if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded "LIBSOXR); #endif return true; } bool resample_init(char *opt) { char *recipe = NULL, *flags = NULL; char *atten = NULL; char *precision = NULL, *passband_end = NULL, *stopband_begin = NULL, *phase_response = NULL; r = malloc(sizeof(struct soxr)); if (!r) { LOG_WARN("resampling disabled"); return false; } r->resampler = NULL; r->old_clips = 0; r->max_rate = false; r->exception = false; if (!load_soxr()) { LOG_WARN("resampling disabled"); return false; } if (opt) { recipe = next_param(opt, ':'); flags = next_param(NULL, ':'); atten = next_param(NULL, ':'); precision = next_param(NULL, ':'); passband_end = next_param(NULL, ':'); stopband_begin = next_param(NULL, ':'); phase_response = next_param(NULL, ':'); } // default to HQ (20 bit) if not user specified r->q_recipe = SOXR_HQ; r->q_flags = 0; // default to 1db of attenuation if not user specified r->scale = pow(10, -1.0 / 20); // override recipe derived values with user specified values r->q_precision = 0; r->q_passband_end = 0; r->q_stopband_begin = 0; r->q_phase_response = -1; if (recipe && recipe[0] != '\0') { if (strchr(recipe, 'v')) r->q_recipe = SOXR_VHQ; if (strchr(recipe, 'h')) r->q_recipe = SOXR_HQ; if (strchr(recipe, 'm')) r->q_recipe = SOXR_MQ; if (strchr(recipe, 'l')) r->q_recipe = SOXR_LQ; if (strchr(recipe, 'q')) r->q_recipe = SOXR_QQ; if (strchr(recipe, 'L')) r->q_recipe |= SOXR_LINEAR_PHASE; if (strchr(recipe, 'I')) r->q_recipe |= SOXR_INTERMEDIATE_PHASE; if (strchr(recipe, 'M')) r->q_recipe |= SOXR_MINIMUM_PHASE; if (strchr(recipe, 's')) r->q_recipe |= SOXR_STEEP_FILTER; // X = async resampling to max_rate if (strchr(recipe, 'X')) r->max_rate = true; // E = exception, only resample if native rate is not supported if (strchr(recipe, 'E')) r->exception = true; } if (flags) { r->q_flags = strtoul(flags, 0, 16); } if (atten) { double scale = pow(10, -atof(atten) / 20); if (scale > 0 && scale <= 1.0) { r->scale = scale; } } if (precision) { r->q_precision = atof(precision); } if (passband_end) { r->q_passband_end = atof(passband_end) / 100; } if (stopband_begin) { r->q_stopband_begin = atof(stopband_begin) / 100; } if (phase_response) { r->q_phase_response = atof(phase_response); } LOG_INFO("resampling %s recipe: 0x%02x, flags: 0x%02x, scale: %03.2f, precision: %03.1f, passband_end: %03.5f, stopband_begin: %03.5f, phase_response: %03.1f", r->max_rate ? "async" : "sync", r->q_recipe, r->q_flags, r->scale, r->q_precision, r->q_passband_end, r->q_stopband_begin, r->q_phase_response); return true; } #endif // #if RESAMPLE squeezelite-1.9.9-1395+git20220104.874e4f9/rsc/000077500000000000000000000000001416534420200176635ustar00rootroot00000000000000squeezelite-1.9.9-1395+git20220104.874e4f9/rsc/app.bmp000066400000000000000000003134261416534420200211540ustar00rootroot00000000000000BM—6(ººà–wwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffffffffffffffwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffffffffffffffffwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffffffffwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffffffffwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffffffffffwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffff€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffff€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffff€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffff€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffff€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffff€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfff€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ÀÀÀññññññññññññãããÝÝÝËË˲²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ÌÌÌññññññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²ÌÌÌñññññññññññññññññññññññññññããã²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²ÌÌÌññññññññññññññññññññññññññññññÝÝݲ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²ÌÌÌñññññññññññññññññññññññññññññññññÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwww††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÌÌÌñññññññññññññññññññññññññññññññññêêê²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwww††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀêêêññññññññññññññññññññññññ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwww†††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀññññññññññññññññññññññññÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwww††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²êêêñññññññññññññññññññññÌÌ̲²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwww†††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€www††††††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€†††††††††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññ××ײ²²²²²²²²²²²²²²×××××××××××××××××××××ÌÌ̲²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀêêêñññññññññññññññññññññÝÝݲ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€††††††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀññññññññññññññññññññññññÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññññññññÌÌ̲²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€†††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ãããñññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀññññññññññññññññññññññññññññññêêê²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€†††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²×××ñññññññññññññññññññññññññññññññññÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²êêêñññññññññññññññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀññññññññññññññññññññññññññññññññññññêêê²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€†††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÝÝÝñññññññññññññññññññññññññññññññññññññññÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€†††–––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀêêêñññññññññññññññññññññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññêêêñññññññññññññññêêê²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÌÌÌ×××ÝÝÝãããñññññññññññññññÝÝÝÝÝÝÌÌÌÀÀÀ²²²¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€–––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÝÝÝñññññññññññññññññññññêêêÌÌÌññññññññññññññññññÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²ÌÌÌêêêñññññññññññññññññññññññññññññññññññññññññññññãããÀÀÀ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€–––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀêêêñññññññññññññññññññññÝÝÝÀÀÀññññññññññññññññññÝÝݲ²²²²²²²²²²²²²²²²²ÌÌÌêêêññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ××ײ²²™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÌÌÌÀÀÀÝÝÝñññññññññññññññêêê²²²²²²²²²²²²ÀÀÀãããñññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññããã™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññêêêÀÀÀÀÀÀÌÌÌññññññññññññññññññÀÀÀ²²²²²²ÀÀÀêêêñññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€–––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀññññññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀññññññññññññññññññÝÝݲ²²²²²êêêññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ×××ññññññññññññññññññññññññÌÌÌÀÀÀÀÀÀÀÀÀãããñññññññññññññññêêê²²²ÝÝÝññññññññññññññññññññññññññññññãããÌÌÌÀÀÀ²²²²²²¤  ¤  ²²²ÀÀÀÌÌÌÝÝÝññññññññññññññññññ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀ×××ññññññññññññññññññ×××ñññññññññññññññññññññññññññãããÀÀÀ²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ²²²ÌÌÌêêêñññññññññ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀÀÀÀ×××ññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀññññññññññññññññññññññññãããÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀñññññññññññññññññññññññññññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ÌÌÌêêêããã™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝËËËËËËËËËËËË×××ññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ×××ññññññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññññññññññññññññññññããã²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀêêêññññññññññññññññññññññññÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ×××ññññññññññññññññññññññññññññññññññññêêêÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññÌÌÌËËËËËËËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌññññññññññññññññññññññññãããÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀññññññññññññññññññññññññññññññññññññ××ײ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËãããñññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËËËËÀÀÀÀÀÀÝÝÝññññññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããñññññññññññññññññññññññññññññññññÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††–––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËÌÌÌãããñññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËËËËêêêññññññññññññññññññññññññÌÌÌÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÝÝÝññññññññññññññññññññññññññññññêêê²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††–––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËÌÌÌÌÌÌÌÌÌãããñññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌññññññññññññññññññññññññêêêÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀññññññññññññññññññññññññññññññããã²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††–––––––––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌãããñññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÝÝÝññññññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀêêêñññññññññññññññññññññññññññÝÝݲ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌãããñññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌêêêññññññññññññññññññññññññÌÌÌÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÝÝÝñññññññññññññññññññññññññññÝÝÝÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††––––––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌãããñññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññêêêÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀñññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññêêꤠ ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††––––––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌêêêñññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌ×××ññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌãããññññññññññññññññññññññññãããÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀêêêñññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††–––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌêêêñññññññññññññññññññññãããÌÌÌÌÌÌ××××××ÝÝÝññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌêêêññññññññññññññññññññññññ×××ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÝÝÝñññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††–––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌêêêñññññññññññññññññññññããã××××××××××××ÝÝÝññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××ñññññññññññññññññññññññññññËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌñññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††–––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌêêêñññññññññññññññññññññããã××××××××××××ÝÝÝññññññññññññññññññññññññ××××××ÌÌÌÌÌÌÌÌÌÌÌÌãããññññññññññññññññññññññññãããÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀêêêññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌêêêñññññññññññññññññññññããã××××××××××××ÝÝÝññññññññññññññññññññññññ××××××××××××ÌÌÌÌÌÌêêêññññññññññññññññññññññññÝÝÝÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÝÝÝñññññññññññññññññññññêêêããããããããããããããããããããããããããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝãããñññññññññññññññññññññêêꤠ ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––†††††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××êêêñññññññññññññññññññññããã××××××××××××ÝÝÝññññññññññññññññññññññññ×××××××××××××××ÝÝÝñññññññññññññññññññññññññññÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌñññññññññññññññññññññ×××ÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀñññññññññññññññññññññÝÝݤ  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××êêêñññññññññññññññññññññããã××××××××××××ÝÝÝññññññññññññññññññññññññ×××××××××××××××ãããññññññññññññññññññññññññãããÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀñññññññññññññññññññññ×××ÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÌÌÌñññññññññññññññññññññÝÝݤ  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××êêêñññññññññññññññññññññããã××××××××××××ÝÝÝññññññññññññññññññññññññ×××××××××××××××êêêññññññññññññññññññññññññÝÝÝÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀêêêññññññññññññññññññãããÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²×××ñññññññññññññññññññññÌÌ̤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××êêêñññññññññññññññññññññããã××××××××××××ÝÝÝññññññññññññññññññññññññ××××××××××××ÝÝÝñññññññññññññññññññññññññññ×××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÝÝÝñññññññññññññññññññññÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²êêêñññññññññññññññññññññÀÀÀ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××êêêñññññññññññññññññññññããã××××××××××××ÝÝÝññññññññññññññññññññññññ××××××××××××ãããññññññññññññññññññññññññêêêÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌñññññññññññññññññññññ×××ÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÌÌÌñññññññññññññññññññññêêꤠ ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××ÝÝÝêêêêêêêêêêêêêêêêêêêêêÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝêêêêêêêêêêêêêêêêêêêêêããã×××××××××××××××ãããããããããããããããããããããÝÝÝ×××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀêêêñññññññññññññññññññññÌÌÌÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀêêêñññññññññññññññññññññ××פ  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ×××ññññññññññññññññññññññññ×××ÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀêêêññññññññññññññññññññññññ²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããññññññññññññññññññññññññêêê×××ÌÌÌÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌãããñññññññññññññññññññññññññññ××פ  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀêêêññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññããã²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌêêêññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññêêê²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌêêêññññññññññññññññññññññññññññññññññññññññññññññññññññññññññññêêê²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝãããããããããããããããããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀãããññññññññññññññññññññññññññññññññññññññññññññññññññññññ××ײ²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌÝÝÝññññññññññññññññññññññññññññññññññññññññññ×××ÀÀÀ²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝããããããÝÝÝ×××××××××ÌÌÌÌÌÌÌÌÌ×××××××××ÝÝÝããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌ×××ããããããññññññññññññãããããã×××ÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝãããÝÝÝ××××××ÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌ×××ÝÝÝÝÝÝãããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝãããÝÝÝ×××ÌÌÌÌÌÌÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÌÌÌÌÌÌ×××ÝÝÝãããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝãããÝÝÝ×××ÌÌÌËËËÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀËËËÌÌÌ×××ããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝããã×××ÌÌÌËËËÀÀÀÀÀÀ²²²²²²¤  ¤  ¤  ¤  ¤  ²²²²²²ÀÀÀÀÀÀËËËÌÌÌÝÝÝãããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝãããÝÝÝ×××ÌÌÌÀÀÀÀÀÀ²²²¤  ¤  ™™™™™™™™™™™™¤  ¤  ¤  ²²²ÀÀÀÀÀÀÌÌÌ×××ÝÝÝãããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝããã×××ÌÌÌÀÀÀÀÀÀ²²²¤  ¤  ™™™²²²×××ÝÝÝÌÌ̲²²™™™¤  ¤  ²²²ÀÀÀÀÀÀÌÌÌÝÝÝãããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××MMM×××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝããã×××ÌÌÌÀÀÀ²²²²²²¤  ™™™ÌÌÌñññññññññññññññ²²²™™™¤  ²²²²²²ÀÀÀÌÌÌ×××ããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××MMM×××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²–––™™™²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝãããÝÝÝ×××ËËËÀÀÀ²²²¤  ¤  ²²²ñññññññññññññññññññññ¤  ¤  ¤  ²²²ÀÀÀËËË×××ÝÝÝãããÝÝÝÝÝÝÝÝݤ  ffffffÀÀÀ××׆††fffwwwMMM××××××ffffffwwwfffÌÌÌÌÌ̆††ffffff¤  ÌÌÌ™™™ffffff†††ÀÀÀMMMfffffffffÀÀÀ²²²fffffffff²²²™™™UUUfff___€€€²²²²²²___wwwfffwww²²²fff–––²²²___†††²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝãããÝÝÝÌÌÌËËËÀÀÀ²²²¤  ™™™×××ñññññññññññññññññññññÀÀÀ™™™¤  ²²²ÀÀÀËËË×××ÝÝÝãããÝÝÝÝÝÝÝÝÝÝÝÝÝÝ݆††–––²²²fff××××××MMM××ײ²²fff××ײ²²fffÌÌ̤  wwwÌÌÌÌÌÌÀÀÀÀÀÀ___ÀÀÀÀÀÀÀÀÀÀÀÀ†††wwwÀÀÀÀÀÀÀÀÀ€€€†††ÀÀÀÀÀÀ²²²™™™fff²²²²²²fff™™™†††www²²²²²²UUU¤  ¤  ___€€€www²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝãããÝÝÝÌÌÌÀÀÀÀÀÀ²²²¤  ™™™ÝÝÝñññññññññññññññññññññÌÌÌ™™™¤  ²²²ÀÀÀËËËÌÌÌÝÝÝãããÝÝÝÝÝÝÝÝÝ××׆††___ÀÀÀ––––––××××××MMM××ײ²²fffÌÌ̲²²fffÌÌ̆††fff††††††™™™²²²___†††††††††ÀÀÀÀÀÀwww†††ÀÀÀÀÀÀfffwww††††††™™™™™™fff²²²²²²€€€€€€€€€€€€²²²²²²fff–––²²²†††UUU¤  ²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝãããÝÝÝ×××ËËËÀÀÀ²²²¤  ™™™×××ñññññññññññññññññññññÀÀÀ™™™¤  ²²²ÀÀÀËËË×××ÝÝÝãããÝÝÝÝÝÝÝÝÝ™™™wwwÌÌÌÝÝݤ  €€€××××××MMM××ײ²²fffÌÌ̲²²fffÌÌÌ™™™€€€²²²–––wwwÀÀÀfff¤  ¤  UUUÀÀÀÀÀÀÀÀÀfff–––ÀÀÀwww†††™™™www†††™™™fff²²²²²²www††††††www²²²²²²UUU¤  ¤  UUUfff†††²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝãããÝÝÝ×××ËËËÀÀÀ²²²²²²¤  ²²²ñññññññññññññññññññññ¤  ¤  ²²²²²²ÀÀÀËËË×××ÝÝÝãããÝÝÝÝÝÝÝÝݲ²²fff†††²²²×××fff††††††MMM××ײ²²fffÌÌ̲²²fffÌÌÌÀÀÀfff†††fff²²²ÌÌÌ€€€†††www†††ÀÀÀfffffffffUUUÀÀÀ²²²fff†††___²²²™™™UUU†††www___²²²²²²___wwwfffwww²²²wwwwww²²²___™™™²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝããã×××ÌÌÌÀÀÀ²²²²²²¤  ™™™ÌÌÌñññññññññññññññ²²²™™™¤  ²²²ÀÀÀÀÀÀÌÌÌ×××ããããããÝÝÝÝÝÝÝÝÝÝÝÝËË˲²²ÝÝÝ××××××ÀÀÀÀÀÀÀÀÀ×××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ̲²²ÀÀÀÌÌÌËËËËË˲²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²™™™²²²ÀÀÀ™™™fff¤  ™™™²²²²²²²²²²²²–––™™™²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝãããÝÝÝÌÌÌÀÀÀÀÀÀ²²²²²²¤  ™™™²²²ÌÌÌÝÝÝÌÌ̤  ™™™¤  ²²²²²²ÀÀÀËËËÌÌÌÝÝÝãããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ™™™fff²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝãããÝÝÝ×××ÌÌÌÀÀÀÀÀÀ²²²²²²¤  ¤  ¤  ™™™™™™¤  ¤  ²²²²²²ÀÀÀÀÀÀÌÌÌ×××ÝÝÝãããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝãããÝÝÝ×××ÌÌÌÀÀÀÀÀÀ²²²²²²²²²¤  ¤  ¤  ²²²²²²²²²ÀÀÀÀÀÀÌÌÌ×××ÝÝÝãããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝããããããÝÝÝÌÌÌÌÌÌÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÌÌÌ×××ÝÝÝããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝããããããÝÝÝ×××ÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌ×××ÝÝÝããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝããããããÝÝÝ×××ÌÌÌÌÌÌÌÌÌËËËËËËËËËÌÌÌÌÌÌÌÌÌ×××ÝÝÝããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝããããããÝÝÝÝÝÝ×××××××××××××××××××××ÝÝÝÝÝÝããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝãããããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝãããããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝãããããããããããããããÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ××××××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××××××××××××××××××××ÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝ×××××××××××××××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––†††††††††††††††††††††††††††††††††–––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††–––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††–––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ×××××××××××××××××××××××××××ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††–––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††––––––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††–––––––––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††–––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËËËËËËËÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌËËËËËËËËËËËËËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††–––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀËËËËËËËËËËËËËËËÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††€€€–––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††€€€€€€––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€–––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€†††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€†††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€†††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€†††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€††††††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€†††††††††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€†††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€www††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€www††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwww†††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwww††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwww†††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwww†††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwww††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwww€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwww€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfff€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ¤  ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfff€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffff€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffff€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffff€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffffwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffffffffffwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffffffffwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffffffffffffffffwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffffffffffffffwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffffffffffffffffffffffwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffffffffffffffffffffwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffffffffffffffffffffffffffffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffffffffffffffffffffffffffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffffffffffffffffffffffffffffffffffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwffffffffffffffffffffffffffffffffffffffffffffffffwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€†††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††††€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwfffffffffffffffffffffffffffffffffffffffffffffffffffsqueezelite-1.9.9-1395+git20220104.874e4f9/rsc/icon.icns000066400000000000000000001230061416534420200214730ustar00rootroot00000000000000icns¦ics#H?þþþüüüÿüÿþ?þþþüüüÿüÿþis32û¤ÿ„‚ÿ>¶uywx”‰x<ÿÿ!%&*/5;:890Aÿ #Wÿ ,/ ## !,hÿ€ :y“¬µ®¯8®®¬­†Pˆ…¼ÉÉÍÐÕØÝãéñ²U¿J™¯¶½ÂÎÖ×Ýâè–Kaÿ'2@LXhw‡š­½f.O0ÿ…(‡ÿ€ ÿ¤ÿ„‚ÿO¶uywx”‰x<ÿÿ!%&+/5;:891Dÿ $Zÿ 6:'*,&).pÿ  “²¹²²³€²7±®®‰Vˆ™ÃÏÏÑÔÙÝâèíôµ\¿Jžµ¼ÂÈÓÚÜáæìšRa(4AN[kzбÁi1U2ÿ…*…ÿÿ€ ÿs8mk &-5>JY75ÁÍÖßçíò÷ûÿÿùN}ÿÿÿÿÿÿÿÿÿÿÿÿ]›ÿÿÿÿÿÿÿÿÿÿÿüBºÿÿÿÿÿÿÿÿÿÿÿñ.Ùÿÿÿÿÿÿÿÿÿÿÿã(îÿÿÿÿÿÿÿÿÿÿÿÍOýÿÿÿÿÿÿÿÿÿÿÿÔ*'¢ÉÍÙäíöýÿÿÿÿüÞz *7DTgyŒ©¥sT= ICN#ÿðÿÿøÿÿü?ÿÿø?ÿÿø?ÿÿø?ÿÿø?ÿÿø?ÿÿøÿÿøÿÿøÿÿøÿÿðÿÿðÿÿøÿÿþÿÿÿÿÿÿÿðÿðÿÿøÿÿü?ÿÿø?ÿÿø?ÿÿø?ÿÿø?ÿÿø?ÿÿøÿÿøÿÿøÿÿøÿÿðÿÿðÿÿøÿÿþÿÿÿÿÿÿÿðil32L›ÿ›ÿ›ÿ›ÿ›ÿ”ÿ€ÿÿ’"3ÿÿÿÿª‡_JFUYXTZfsvy„‰Š‰I*g‚ÿ/5<LZC‚ÿÿ3Ž‚‹ÿUŠÿÿ”ÿƒÿ›ÿ›ÿ›ÿ›ÿ›ÿ›ÿ›ÿ›ÿ›ÿ”ÿ€ÿÿ’"3ÿÿÿÿÿª‡_JFUYXTZfsxz„‰Š‰I*_‚ ÿ16<=@CFIORUY]€_ [VSOJC>3U , ŒBR€ÿ#‡‚€#Ni€ ÿ"‚   ,Uo€ÿ6BL[C‚ÿÿ3Ž‚‹ÿUŠÿÿ”ÿƒÿ›ÿ›ÿ›ÿ›ÿ›ÿ›ÿ›ÿ›ÿ›ÿ”ÿ€ÿÿ’3ÿÿÿÿª‡_JFZ][V\kxz~‚ˆ€‹J.g‚ÿ19>ADFKLQTX[^`a`\WSOKD>8U ,  ŒFV€ÿ#‡‚€%Rn€ ÿ$!'# !- ‚ ( .Wo€ÿ@PGQHAG4-6-CP7:_€ÿ+I=RE>L\D!‚ÿÿ3Ž‚‹ÿUŠÿÿ”ÿƒÿ›ÿ›ÿ›ÿ›ÿl8mk  (3?KXcmwš¥°ºÄÍÙØŠ wÑÔÚáèîòõøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÕ!4÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿA]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó.zÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿã ›ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿѹÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½ Óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ©%æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿHóÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿudýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû\‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòB¦ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿê(ÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜ+Îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæ]#šñýûüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïÁŒ/H޶»¼ÃËÑÚàæìñõúýÿÿÿÿÿÿÿÿÿÿùúúë‚)DKJQ[ep{„˜¢®¸ÁËÔÜãêöôëÞ¿¬¥”m !(09CMXcny‰¢¦wU:2* !48& ich#H€ÿÿÿàÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿàÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿàÿÿÿÿàÿÿÿÿÀÿÿÿÿÀÿÿÿÿàÿÿÿÿüÿÿÿÿÿ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?ÿÀ€ÿÿÿàÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿðÿÿÿÿàÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿà?ÿÿÿÿàÿÿÿÿàÿÿÿÿÀÿÿÿÿÀÿÿÿÿàÿÿÿÿüÿÿÿÿÿ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?ÿÀih32 `ÿÿÿîÿÿ -D-" *7;2Jÿ‰%qmkfWG336AHNKHJMU_inrv|ˆŒ“𠤩¯´{.[¶…k9;BDFJNRV\`djotz}~€}{xsmg`[VPIB:3*)>ÿƒÿ!"! ’55ƒf¡BEƒJ¡#EŨ$І †+H\ÿ‚ÿ) &+'),6#%,":-…€- ƒ2L`ÿ‚ÿ"€4%$& %  (2%&:Niƒÿ€QZEI^LMT&EWDJOTGV€/-::2€@Qvƒÿ€'  -,$ €&./-€ HUyƒU ¡MY}ƒ;¡Q_žƒL &Vbƃ)™¯±­¨£Ÿœš—”‰…€|yurnid_ZUNHB<730.*& /Ykÿƒ)¬ÅÅÇÊÈÇÊËÍÏÏÓÕ×ÙÚÚÜÞÞàáãäåçèççèèææãÞ¾d9[yÿƒ(¨ÁÁÄÂÁÃÆÆÅÇÈÈËÌÍÏÐÐÓÓÕÖ×ÙÛÜÞàáäåææåá¾]>a~„¡Â¿¿€ÁÃÆÇÈÉÊËÌÍÍÐÑÒÒÓÕÖØÚÜÝàáã€äâܳOCf„ƒ)$ŸÁÀ½¾ÁÁÂÂÄÇÈÉÊËÎÎÏÏÐÑÓÔÖØÙÚÜßßáâããáÚ©EHd’ƒG¢€¾$¿¿ÀÂÃÅÄÇÇÉÊÌÍÎÏÑÒÒÔÕØÚÛÜÞßàáââàÖ=KeŽƒP¬º¼¾À¾¿ÀÁÂÃÄÃÅÈÊÌÎÏ€ÑÒÔÔØÚÜÝßà€áßÑ6PcUƒS©²¶¹¹»€¾ÀÂÀ¼¿ÃÄÈÎØÜØÐÒÕÔØÙÛÝÞßàáàÞÊ€1Vcƒ7„Œ’—œ¡¥ª®²µ·¹¼¿ÂÅÊÏÕÓÐÑÓÒÕ×ÙÚÛÜ€ÞÜÃp.Ybƒ-Mnty~‚…ˆ‹’•™£§¬¯µº¿ÃÈËÎÑÔרÛÝÜÜÙ´]2dVLj\O+ &*3>HU^hpz‚ˆ’–˜š› £¦ª¯¶º¾ÄÆÉÎÅ•K"9;U}Š˜—‹! $/-4§ÿÿÿàÿÿÿîÿÿU€ -32" *792Jÿˆ&ÿqmkfWG3/6AHNKHJOX_jorw}‚ˆŽ“𠤩¯µ|0[‘…(†9Pj’ƒP­»½€¿"ÀÁÂÃÄÃÂÄÇÉËÍÎÏÐÑÔÔ×ÙÛÝÞàáââáàÑ8UhUƒS¨³·ºº½€¿Áÿ½ÀÂÃÇÐØÜØÏÑÕרÚÜÝßà€áßÉ€3[fƒ)7…Œ“˜œ¢¦«¯³¶¸º½ÀÃÆÉÑÕÒÎÐÒÔÔØÚÛÜÝÞßßÝÂp0^e!ƒNouy~‚†‰‹Ž“–šž¤¨­°¶»¾ÂÆÊÍÐÔØÙÜ€Ý Ú´^4g[NoaQ#+ &*3?HU^hpz‚ˆ“–™šœž¡£§«°µ»¿ÃÇÊÍÄ–K$;=W›˜‹! $/,K<…96;! ƒ6Rcÿ‚ÿ"€ I+$553$2$ GO:7@Vmƒÿ€!x‡emsv€6 g€dly€l'€ ?PdaU € HY}ƒÿ€ +"#:)(*!,+!@?1 €*ANKL €N^ƒU œT`†ƒA¡ ZežƒL )^jƃ)žµµ±­¨¢Ÿš—’Œ‰‚~{xtokfa[UOIC=731.*& 3asÿƒ±ËËÍÎÌÍÐÑÒÔÔ×ÙÛÞßßáããåæçèé€ë êëëêèæáÂf=d~ÿƒ¬ÇÇÈÈÆÉÊÊËËÌÎÐÐÑÓÔÕØØÙÚÛÝßàâãåæè€éäÂ_Cj‡„ªÈ€Å#ÇÇÉÊÌÌÍÎÏÐÑÑÔÕ×רÚÛÜÞàâãäææççæà·QInŠƒ-¥ÇÄÃÄÇÇÈÈÊËÌÍÏÏÑ€Ó ÔÖØÙÛÝÝßáãã€åæåÞ«GNl•ƒK¨€Ä$ÅÅÆÈÉÉÊËÍÍÏÐÒÒÓÕÖרÚÜÝÞáããääæåäÙŸ@Sm’ƒU²À€ÄÅÆÇÈÉÈÇÉÌÎÐÒÓÔÕÖ×ÙÚÜÞàâãäãÔ’:XkUƒU®¸¼¾¿Â€ÄÆÈÃÁÄÇÇËÒÛÞÜÔÖØÙÛÝÞàâãâÍ‚5^iƒ);‰’—¢§«°´¸»½¿ÁÅÈÊÎÔØ×ÔÕ××ÙÛÝÞßàáââàÅr3ah!ƒ-Qrx|‚…‰’•—›Ÿ£©®²µ»ÀÃÈÌÏÒÕÙÛÜßààáÝ·`7j^RrdT#+ '+4@IVajr}„‹–™œž¡£¥¨«°´ºÀÄÈÌÏÓÈ—M'=@\„’¡ž‹! %0=KXes€Œ–ž¥ª±´·¹»½¯‡5+1'"0T\C—'4CSap}…@06§ÿÿÿàh8mk  "-8BO_m}™g   '-6BN[hvƒ¨²½ÇÑÛäìðöûÿÿÿÿÿÿÿÿÏY¡ÜÞâèíóùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœ¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØ ZÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁzÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿªšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp ÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTêÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ9/úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿñ%MÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãyÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿΚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™Õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ^@úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷-ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿé7!…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô{<!dâÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóɘg3I’ÊäñéêîòöøûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔH (X…¡°²±³¸½ÂÇÍÓØÝâçìðôøûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö÷øîìÌz !;Scfdfmt{‚‰‘—ž£©¯´ºÀÇÌÒØÞäéïòöùýÿÿýùôäÎÅÁ¼µŸy  #(-39?FLSZahpx€ˆ–ž¤«²¸½ÃÏäàÒȾ®˜ƒzun_E  &+17>EMT\dlt~Œœ£™‡wcJ5,)$ #)0=NTI8+   it32?Ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ™ªy‹[úHÉq(ôȪ“á÷Øÿ”q³Šîã+züL⻘|cM9* † *¯Âÿ<H¿ÉÆ‘îч ´n4ÿÓ±”~iVG<3(  !)09CNYcmy„™™rD?®ÿPâfnG[,?}´i$ïã…kTD5+    (2;GR]iv‹”Ÿ¬¶¾ÈÑÖÜäêîó÷ùüýþþýýüúõçÓ¢ IB±™ÿFëÓD…›%Ø—Z(Ú¼¡†saQE8,# € '4@LWdsŠ˜¡¬¸ÃÊÒÛàåëðõ÷ø&÷õóòñïîìêçåãáßÝÛØÕÒÏËÈÄÁ¼·±©«N Xuî•ÿhæ~-$  ")/8BKT\grxƒŽ˜ ©¯¶»ÃÉÍÏÏÐÐÑÐÎËÇÅÁ¿¾»¹¶¶³°­©¤¡™•‘‰…€|ytpkea\WRKFB<72-*%!*& ŒÚ“ÿI\%29CKRX^djpuz…ˆŒ‘”–—––”“Šˆ„€}ytpmheb^[XTRNJGDA?<852.*&# –$22$û’ÿ/¸Ylkkihfc`]ZWSOLIFB=:740-+'$  ±&004-Z¹ÿ! Ï,5221?.ÿá6<9849gÿâ0à=€@;E]ÿ·à"@@BD@Zÿ–á'BADFBtÿíá ,DAEFB˜ÿ‘á 0DBEGDÈÿWá5DCGIFÿé,á:DCGKGvÿÔÁ •>DEJKLÿ̇  $$ $(0*0=;(•E$==H>2ŽBDEJMT<ÿˆ IE>JVLZ DLDB[_N;3`*1?i9F>FY?-•+K54,?6=$0, 4 DBFKL\vÿ?ˆ LL3I[FIC\H-<<;P9)*@ 8A9OHE;>/•2"3%=1+'!‹%FEJNKnJÿň       # ž #<918(17Š+FEJNKŠHÿuˆ _?+3>N€+1!#€&/M‡&‘ 387'60/8=Š2IEJOK³ÿªAˆ+€/L,SCU>WblZrk\R}93how‰ )G 436;<<828.‰=KGORL<ÿÝ ‡3d=1v0WEPjd[Vtf3=o0FoBStQ(u[lKPnanO$q/Dl[X‰ 3C 7::TOSYW$ÿwáDUQUZ[“ÿîâIURW[`DÿáOTSX\g‡ÿ©ß"RTUY]q Žÿ,­™ª«©¢—’‹†~{tmib_YVSPJFD?;82.*&# ¶(URV[^‡Žÿ‡ž´ÆËÎÏÑÒÓÔÓÒÓÕÕ‚Ö׃Ø6ÙÕÔÔÓÓÐÐÏÍÊÆÂÀ¾¹¶²¯­¨£Ÿš—‹†€|wplea[UPJE@96/*%  .VSW]_¥ªŽÿ™´À€ÃÆÆÇÉÈÇÅ€ÃÅÅÄÆÇÆÆÇ€ÆÇÇÉËÌÍ€ÏHÐÑÒÓÔÖØÙÚÛÜÝÞàââãäåççéêêëìëëíìëëêêçæäâÞÛØÔÒÍÊÇÿ¼¶³¯©¥Ÿ™•‡saM:"4WSX^aÔÿ˜¶ÀÂÃÅÇÈÆÅ€ÃÄÄÆÅÇÈÇ‚È ÇÈÉÉËÌÍÎÏÐÐÑÐ‚Ñ ÐÑÑÒÓÓÔÔÓÕ€ÖרÙÙ€Ú*ÛÜÝÞßááââãâäçéêëîíîîïñóôõõôóòíãѳ“\!9VSY`cÿ·™¹ÃÄ€ÃÄÃÃÂÁÂÃÅÆÄÆÇÈÉÉ€ÈÊÌ ÍÎÎÐÐÑÒÑÐÑÒÒƒÓÔÔÕ€Ö€×€Ù ÚÜÝÞßàááâããâ€ãåæçæåææääãáÛо¢ƒO?VU[agÿA¹¿ÁÂÃÃÄÅÄÃÁÁÂÂÃÃăÅÆÇÈÉÊËÍÎÍÏÎÎÏÐÑÑÒÓÔÔÕÔÖ××Ö×רÙÚÛÛÚÛÛÜÝÞß߀àáãääå忀åääãâßÚι~HEUV]cl&ÿ ö¡º¾¿ÁÁÂÃÄÅ€ÄÂÅÇÈÇÇÆÆÇÇÈÉÉÊÊËÌÌÍÍÎÎÏÐуÐÒÓÔÔÓÔ€ÕÖÖ×רÚÚÜÛ ÜÞÞßààßááâäãäåæåääãâàÚÍ·›y@MUX^es.Žÿ È¥¼¾ÀÂÃÄÅÄÄÀÂÁÃÅÆÇÇÆÅ€ÆÇÇÆ€ÇÈ€ÉËÌÍ€ÌÍÎÏÏÐÑÒ€ÓÔÕÔÕÖÖØØÚÚÛÜÜÝ߀àââäææååææåãâáßØËµ˜u9QTY_f|OŠÿë着½ÀÁ ÂÁÃÂÁÁÀÁÁÃÄÅÇ‚ÆÈÇÇ€ÉÊÉ€Ë ÌÍÍÏÏÑÒÑÓÒÑÒÔÓ€ÕÖרÙÚÛÜÝ€Þ&ßÞßààáããääãäåääæåäãâàÝÖȯ’n1"UT[ahŽUÿüñ‡ë€ 5›°½¾À¿ÀÁÀÁÂÄÃÁ€ÂÄÅÅÇÈÈÉÈÉ€ÊËËÌÍÎÎÍÌÌÍÍÏ€ÑÐÑÒÒÓÓÔÕÕÖÖרØÙÙÚÚÛÜÝÝßà€áâ€ãäåäãââààÜÔÅ«h+(WT]ch¦Hÿâl‡Ìœ±¾ÀÀ¿ÀÀ¿/ÁÃÃÂÂÃÂÄÆÄÅÅÆÈÉÈÉÊÊÈÊÊËËÌÌËÌÍÎÍÌÌÍÎÎÏÏÐÐÑÒÓÒÓÓÔÔ€Õ Ö×ÖרÙÚÛÛÜÜÞ߀àáââãâ€ãäåäãâáààÛÒ§Š_#.WT\aeÉÿÿàa‹ ³½¾¾¿¿À€¾½ÀÁÂÂÁ€Ã ÄÄÃÃÄÅÆÆÇÇÈÊ ËËÌÍÌÍÎÎÏÏÐÏ ÎÏÐÑÑÒÒÓÔÔ€ÕÖÖ×ØÙ€Ú ÛÜÜÝÞßààáââ‚ãäââáàÛÑ¿£…Y!5UT[aeÿÿàc‹™£´½ÀÂÀÀ¿¾¿ÀÀÁÁÂÂÁÂÂÁÂÂÁÃÅÄÅ€Æ ÇÇÈÈÉÊÊËËÌÍÎ΂ÏЀÏÐЀрÒÓÔÖ××ØÙ ÚÛÛÜÝÞßßÞÞßßàá„âáàßÞÙÏ»Ÿ‚SUW_asnAsy|~ƒ†‰‹Ž‘“”—™›Ÿ¡¢¥§©­¯²³µ¶·¸¹º¼¼½ÀÁÃÄÄÅÅÇÇÆÇÉÉËËÍÎÏÍÍÎÎÏÏÐÑÑ€Ò ÓÒÓÔÕÖ×רÙÙÚÛÛÜÝ€Ü󯄬Šk<EUY_bkt;6\xsrtvy{}€‚‚„†‡ˆŠŒ‘’“•–—™›œž ¢¤§©¬¯±³µ·º½¾ÀÁÃÅÆÇÈÈÉÉ€ËÌÍÎÏÐÑÒÓÓÕÖׂØÚÛÜÜÝÞÝÞÞÝÜÜÛ×e4 KV[_dlHR=# Œ \yzwvut€s4uvxzz|~€„…†ˆˆ‰Š‹Œ‘’“•–˜š›Ÿ¡¤§©¬®°²³µ¸º½ÁÃÃÅÆÇÈÉÌ΀ÏÐÑÒÑ€Ó ÔÖ×ÙØÙØÙÛÜÝÜÛÛÜÛÚÖ˸š}_- RW[_dh=Imf_YWL<ˆGhqy~~‚„ƒ~€|€{4||€ƒ„††ˆ‰ŠŒŽ‘“•—˜™›œžŸ ¢¤§©«®°±²µ·º¼¿ÁÃÅÇÊÊÌÎÐÑÓ€ÔÖÖ×ÙÙÚ€Û ÜÛÛÚÚØÑÄ®‘uX%[Z`enk(Ÿ£–Œvmih]B† "(;;?JV^fnv|‚ˆŒ‘Ž‹Šˆ†…„‚€‚T…†‡‰Š‹Ž‘’’“•–˜š›œžŸ ¢¤¦§©«­°±³¶¸º½ÀÃÅÇÊËÍÎÐÑÒÓÔÔÕÕÖרÙÕʹ¢…jR#iccafV g~’¥°²­¦Ÿž—nŠr &-4;EOZcmw†Œ‘–˜š›š˜–“‘ŒŠ‰ˆ‰‰ŠŠŒŒŽ‘“”–˜š›œžŸ¡£¤¥§©ª¬¯²³¶¹»¼¾¿ÀÂÃÅÇÈÉËÌÏÎȽ«’ycR$"Apl\QOYi}”±âÿœ‹‰  $*3=GS]it}‡— £¦¥¤¡žœ™–”’€@‘’““•—˜™œ ¡¢¤¥¦¨©«­®°±³¶·¸º¼¼½¿À¿¹­›…phH#(),iUJcv…yj]SRb›£" $,5@KZguƒŽ—¤©­®¯®«©¦¢Ÿœ›™˜€™šž ¡£¤¦§¨ª«­®¯°²³´µµ³« |rx,,€-96H‘˜ž¡›zfT²= &2>JYgv„š¢ª°µ·¹¹·µ³°¬ª¨¦¥¤¤¥¦§¨ª«­®¯°°®¥š‹ƒ‹A*11237) *€/#)7-8IZP5¾/ !*4=IWet‚›¦°·¾ÂÅÆÅÿ¼¹·µ³²±®¦Ÿ—F&778;C&€ Î% '/9GTapš¦±ºÁÇËÏÑű„,4LNOPJ-à ")2=CP^X5"IPC7' ë   ô ÿÿÿÿÿÿÿÿÿÿ‡ýýñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢ÿ™ªy‹[úEÉq(òÆ©“œà÷Øÿ”q¼Šîã+zùšL໘zaL8) † *®Âÿ¿ÉÆ‘îч ±l4ûÓ°”}iUG;0& € !*1:COYdnz…š›sEF®ÿ=âfnG[,;z²g$ïã…kSB5*   !)3=HS_kxƒ•¡­·ÀÉÒ×Ýåëïô÷úü€þýýüúõçÓ¢JB±™ÿ9FëÎ?…›%Õ—X(ؼ¡†r`QD8,"  (4AOXft‚Œ™¢®¹ÄËÔÜàåìðõ€ø(ùø÷õóòðïîìêçåãáßÝÛØÕÒÏËÈÄÁ¼·±©«N Yuî•ÿhæ~-$  #)0;DLU^hsy„˜¡ª°·¼ÄÉÍÐÏÐÐÑÐÎËÇÅÁ¿¾»¹¶¶²°­©¤¡™•‘‰…€|ytpkea\WRKFB=72-)%!+' Ú“ÿ\%2:DLRY_dkqvz€…ˆŒ’”–.”“Šˆ„€}ytpmhfc_[XURNJGDA?<852.*&# –%34&û’ÿ¸Z€l(jhgda^[XTPMIFB>;841.+(%" ±'115.[¹ÿ! Ï-5332@9ÿá7=:95:gÿð1à>€AFc?4•*I18)?9A'3- :!FFJOO_vÿ?ˆ UY:TiPPGgO1GCDX:(+C 8H=PKE;:0•3.G"1 6,‹&HGKPOqJÿÅŠ    ž /MI.;89=Š-KHMQOŒHÿuˆ "rG(7K\€(0$"€.5Y‡2 ‘ =KI,C?CE?Š 3MJNRN´ÿªAˆPOˆFB*d9L°ASJY„8OF-€<]3d$PbJB!k02-k@" DG‰/j@FHMJGXIBD9 ˆ 9NJPSOíÿ ˆ2ot‹’ž… Z{YŒU¡|uZt‹’Žƒ".(fi>•¥uJiyŒp’‡to¡E@„Œ— ‰ ,U FIGLJHFBI6‰@NLQTQ>ÿÝ ‡3vK3“5p¹W`{uoš|?M˜>R‚Ha“g6“h„]`Š~‡X)‹7M‰oc‰ 8D HHGIHGBDFE4‰ENNRVU¿ÿɈ \(‹.|[nWt\qŸŒrT|‰1dn€¥?ˆljA”eg}U}£f˜‰G~]ˆl•JEEGIIABGJ;‰JOOTW\–ÿ†ˆ     #i'‰C8GHDHFF?BKI,ˆOOPUXdÿé´‘ !%$&"#'')ˆ#QORWYpUÿá+SOSXYˆÿSá 2UPTYY¬Žÿ¹*á 9XRV[ZÝÿá@XSW\['ÿwáFYUY^_—ÿîâKXV[_dGÿáRWW\`k‹ÿ©ß#VWY]au Žÿ,®šª«©¢–’‹†~{umjb_YVSPJFD?;82.*&# ¶)YWZ_b‹ŽÿR‡ µÆÌÏÐÑÒÔÔÓÓÔÕÖÖ×Ö×ÖררÙÙØÙÖÕÔÓÓÑÐÏÎÊÇÂÀ¾¹·²°­¨¤ š—‹‡€|wplea[UPJE@96/*$  0YW[adªªŽÿšµÁ€Ä6ÆÆÈÉÈÇÆÄÃÄÅÆÅÆÇÆÆÇÆÆÇÈÇÈÊËÍÎÎÏÏÐÒÓÔÖØÙÛÜÜÞßáãâãäæèèéêëë€ì,ííììêêèæäâßÜÙÔÑÍÊÇÿ¼¶³¯©¥Ÿ™•ˆsaN;"5YW\beÖÿ™¸ÁÃÄÅÇÈÇÆÄÅÇÆÈÈÇ€ÈÉÉÇÇÈÉÊËÌÎÏÐÐÑÑÒÑÑÒÑÑ€Ò‚ÔրרÙÚÚ€Û*ÜÝÞÞàáâããäãåçéêìîíîïðòóôõõôôòîäѳ“]!:XW]dgÿ¹›ºÃÅÄÄÃÄ€ÅÄÄÃÂÃÃÆÇ ÅÇÇÉÊÈÇÈÉÉË ÍÎÏÏÐÑÑÐÏÑÒÓ„ÔÕÖ× ØØÚÚÛÛÜÝÞààááâ‚ãäåæçæåææååäâÜѾ¢ƒPAXY_fk…ÿAžºÀÂÃÄÄ€ÅÄ€ÄÅÅ‚ÆÈÈ€ÉÈÉËÌ΀ÌÍÏÏÎÏÎÍÎÐÒÒÓÓÔÕ€ÖØ×רØÙÚÛÜÛÛÜÜÝÞ߀à€á€âãääååæ‚åäãàÛι~IGYZagp&ÿ÷¢»¿ÀÁÂÃÄÅÄÄÃÃ‚Æ€È ÇÆÆÇÇÈÈÉÊÊ€ËÍÍÎÎÏÐÐÏÐÏÏÐÐÓÓÕÕÔÕÕÖÖ×רØÙÛÛ‚ÜÝÞßàâáã€äåæå€æååäãàÚη›zAOY\biw5ŽÿȦ½¿ÁÃÄ€ÅÄÄÂÂÃÂÄÆÆÇÇÆÆÇÇÆÈÇÇÈÉÉÊËÌÌ€Ë ÌÍÏÏÐÐÏÑÑÓÓ€Ô€Õ€ÖרÙÙÚÛÜ ÝÝÞßàááâãä€åƒæåäãâßÙ̵™u:TY]ck€OŠÿëï««¾ÁƒÂÃÀ ÁÂÄÅÆÆÅÆÇÆÆÇÆÇ€ÈÉÊÊÉÉÊ€Ë ÌÌÎÏÑÒÑÓÓÑÓ€ÔÕÔÕÕÖրרÙÚÚÜÜÞ€ßà߀àâããääãä€åæååäâáÞÖȰ“n2#XX_el‘Uÿüñ‡ë€5œ±¾¿ÁÀÁÂÁÀÁÁÂÃÅÄÂÃÄÄÅÄÆ€ÈÉÈÈÊÉɃËÌÍÍÌËËÌÍÏ€ÑÏÑÒÐÑÒÑÔÔÕÕÖ×רÙÙÚÚÛÛÜÜÞÞààááââ€ãäååäãââáÝÕŬh,)ZYaglªHÿâl‡̲¿‚ÁÀÂÄÄÃÃÄÃÅÆÄÆÆÇÈÉÉÊÊÉÈÊÉÊ€ËÊËÍÎÍ€ÌÍÎÐÐÏÐÒÒ€ÓÔÕÖררÙÚÛÜÜÝÝßàááàá€ãâã€äåääââáàÜÓ§Š_$/ZX_eiÍÿÿàa‹’¢´¾€¿ÀÀ€¿¾ÁÂÀÄÅÅÄÄÅÆÇÇ€ÈÉ€ÊËÊÌÍÎΆÏÒÓÕÕ€Ö×רÙÚÚÛÛÜÝÝÞÞàáàáââãäãƒäãââàÜÒ¿¤…Y"6YX_ei ÿÿàc‹™¤µ¾ÁÂÁÁÀ¿ÀÁÁÂÂÃÂÂÃÃÂÃÃÂÄÅÅÆÈÇÇÈÉ€ÊËÌÌÍÎÎÏÐÏÎÎÐ€Ñ ÒÓÓÒÔÕרØÙÚÛÜÜÝÞßà€ßààááââãââáàßÚϼŸ‚T >YY`ek\ÿÿàc‹Чµ½¿ÀÀÁ¿À¿À¿ÁÂÄÅÅƒÄ ÅÆÆÈÈÇÆÆÇÈÉÊ€ÉÊÌÍËËÍ̀ΠÌÍÍÎÏÑÑÒÓÔÔÖר‚ÙÚÚÛÜÝÞßß‚à€áââããâááàßÙ͸›}LEXY`fnãÿÿàc‹‰§µ¼¾¿‚ÀÂÁ€ÀÁÂÃÂÅÆÇÈÇÆÇɃÈÇÈÉÊËÌ̃ÍÏÑ€ÒÓÓÒÒÔÕÖ××ÙÙÚÛÜÜÚÚÛÜÝÞßßà‚á€ãäãáàßÝ×˳–wDKXZahsÜÿÿèeŠ "Чµº½¾¿ÀÁÀ¿¿€À$ÂÁÃÃÄÄÅÅÆÅÅÆÅÆÇÈÉÈÈÉÉÊÊËÊËËÌÍÍÎÎÐÐÑÒÒ€ÓÒÑÑÒÓÔÖÖרÙÛÛÜÝÝÞÞßààáââãâ‚ãâàßÝ×ɱ”s>OX[bh{U¶Å¼ZŠ F¨¶»½¾¾Á€ÃÁÁ¿€ÀÂÂÃÂÃÃłƀÈÉÉÊÊÌÌËÌÍÍÏÎÏÎÍÎÏÏÐÏÐÑÐÑÓÔÔ€Õ ÖÖØÙÚÛÜÜÝÝÞàá€âãâ€áßßÜÔÆ­m6 SW\bk‹ÿ‹N”ª¶º»¼¼¾€¿€À ÁÀ¿ÀÀÁÃÃÄÄÃÄÃÄÆÅ€Ä ÆÆÇÉÊÊËËÌÍÍÌ€ËÌÍÎÏÐÒÓÒÒÔÔÓÔÔÖ€×ÙÚÛ€ÜÝÞ߀àáâááââ‚áàßÞÛÒÁ§Šg/$VW]dmƒŽ W™«´·¹ºº»¼¾ÀÁÀ¿ÁÀÁÂÂÃÃÄÄÆÆÅ½»¾Á¿ÁÂÂÅÅÈÉÉËÎÏÑÒÑ‚ÒÓÔ€ÕÖÕ×€Ø ÙÚÚÛÜÛÜÝÝÞ‚ßà€á€âáßÞÙо£†a))XW^ep„DŽ b˜ª³µ·¸º¼¼½¼½¾¿€Á0ÀÀÁÁÂÄÃÄÃÄÄü¿ÂǾÂÄÃÇÁÅÇÈÊÌÑÕÙÞÙÜÚÚÜÒÎÏÐÒÔÕÕÖÖ×ØØ€ÙÚÛÜÜÝÞßßàáâãâáâááàßÝØÍ»žY!/ZW`fsƒl’£±³´µ·ºº»»º¹º»»½¾¿¿‚¾3ÀÁÂÃÄ﻾ÂÀÁÂÆÅÂÆÄÆÉÊÍÓÖÐÙßÙààÔÑÐÏÐÒÓÔÕÔÕ×רÙÙÚÛÜÝÝ€Þàà€á€âáàÞÜ×˶™{Q5ZXafuyP\‰“¢§©«­°±³µµ¶·¸¹ºº¼½½¾½¼»¼½¿ÀÁÁ¾·º¹º½¾¿ÂÀ¼ÂÅÆÊÈËÐÕÖÚÞØÝßÒÏÐÑÑÒÓÓÕÖÕÖÖØØÙØÙÚÛÜÜÝÜ€ÝÞ†ßÝÛÕȱ”vJ;YYbfxhD„Œ‘•˜›ž ¢¤§ª¬®¯±´µ··º»¼¼½¿¿ÀÀÁÁÀÁÁÀÁÃÄÅÆÆÇÉÌÑÖ×ÚÙÜÙÎÏÑÒ€Ó*ÔÔÕÕÖרÙÚÜÛÜÛÛÜÜÝÞÞßàáààßàßÝÛÓÅ­rDAX[cexq*tz}„†‰ŒŽ‘“•˜šœ ¢£¦¨ª®°³´¶·¸¹º»½½¾ÁÂÅÅÆ ÇÇÈÈÉÊËÍÎÏÌÍÏÑÑÒ ÓÓÔÕÖ×רÙÚÚÛÜÜ…ÝÜÙÒ¨Šl=HX]ceow;5\ytsuwz|~€‚ƒ„…‡‰‹ŒŽ’“”–—˜šœŸ¡£¥§ª­°²´¶¸»¾¿ÁÂÄÅÆÇÉÈÉÊ ËÌÍÍÏÐÏÐÒÒÓÔÕ€Öר‚ÙÛÜÜ݃ÞÝÝÜØÏ¾£…e5NZ_cgpIS@$ ŒA ]y{xwvuttuwwyz{}€‚„…†ˆ‰Š‹‹’“”–—˜›œž ¢¥¨ª­¯±³´¶¹¼¾ÁÃÄÅÆÇÈÉÌÍÎ΀ÐÑÑÓÔÔÕרÙÚÜÝÝÞ€ÝÜÜÝÜÛÖ˸›~`.TZ_chl>Jqjd]ZN<ˆHhqy~‚€„…ƒ‚€}}€|?{|}€ƒ„†‡ˆŠ‹ŒŽ’“–˜™šœŸ ¡£¥¨ª­¯±²³¶¸»½¿ÁÄÆÇÉÊÌÎÐÒÓÔÕÕÖרÚÚÛÛ‚ÜÛÛÙÒį‘uY%\\chqo*¢¥›‘…zqmlaB† #(<<@JV^gow}‚ˆŒŽ‘‘‹Šˆ†…„‚Uƒ…†ˆ‰‰‹‘““”–—™šœŸ ¡¢¥¦§ª¬®±²´·¹»¾ÀÄÆÈÊËÍÏÐÑÓÔÔÕÕÖרÙÙÕʹ¢…kS#kdechX !i€•¨³¶±«£¤žrŠ# '-5GT^it~‡—¡¤¦¥¤¡žœ™–”’‘’“””–˜™šœž ¡£¤¦§©ª¬®¯±²´··¹»¼½¾ÀÀ¿¹­›…piI$(*-lYNfy„‡|l]SSdœ”£E $,5@L[guƒŽ—¤ª­®¯®«©¦£Ÿ›š™š™š›œž ¡£¤¦¨©ª¬®®°±²´µ¶¶´¬¡}sy--€.:9K”£¦Ÿ~jX²I '3>KYhw†‘𣫱¶·¹¹·µ³¯¬ª¨¦¥¤¤¥¦¨¨ª«­®¯±±®¥›ŒƒŒA+22348+!+/00$)9/JXft‚›¦°·¾ÂÄÆÅÄ¿¼¹·µ³³²®¦Ÿž˜G'789GPYbmw}ˆ“œ¥­³º¾ÆÌÐÒ€Ñ;ÒÑÏÌÆÄÁ¿¾»¸¶¶²°­¨¤¡™•‘‰…~zvrmgc^YTNHE@:50-)%  .+ŽÎ“ÿ\+6?HOV\bhnsy}ƒ‡‹‘”–˜1–”’ŽŒ‰†‚~zvrojhea^ZWUPMIHDB?;952.+($! “)68* “ÿ¸\€n0ljiec`]ZWROLJFA?;852/,)&"! ©*5592]¹ÿ  €…¹0:776D9ÿÙ;B>=9>kÿâ0à A€E@J]ÿ·à&EEGIF_ÿ™á+GFIKGyÿð·¦ 0IGJMIÿ“á4IHKNKÍÿ[á9JHMOKÿé0á>IINPL|ÿÝÁ •CIJPQQÿ̇  #"20 2$:?2>SG6•Y1IF_Q=GILQRYCÿˆ! gWTdqjz]d_Yx~kP&F…:ATGeO[wN>”8\AB5NGQ0>5B #IIMRRbvÿAˆ fmHf^dVzcDTQQkH47UGYQ d_VLO=•?(;)R(<!E8)‹)KKOSRtJÿɈ      +ž ;d^+,€3=e‡9 ‘ Q``:VRPTKŠ 6PMRUR¸ÿªAˆX]šYO5sJU›ÂQ_(We–Q_R8 €Di>s+Yr"RL1x;?7xL)-QO‰8xR[]c_`i]VRC ˆ;RNSWRðÿ"ˆ2{ƒ‘œ¤¢°—jnžd¬…i€¡¢ž’$1.rvL§¶ž‚Wu‰Ÿ¦™†€·QMšž§"‰5c Y]_`b`ZV^H ˆBROTXTAÿ݇3ˆQ@©B|Éao”‘‰‚ª$H]«Gd’Wo¦t;¥y™"l!nš”œk:¡B^žw‰ ETZ]]`^[YZX]D‰HRQUYXÂÿŇ n2Ÿ8p~b…hвŸ‡]?t€ ·I›|~L¥zu!’b³w«œZ’nˆy¦`YZ^abXX\dL‰MRRWZ_™ÿ†ˆ   &  #,   x#'.‰PC(^^Y^`\SXba?ˆ QSSX[hÿë´"‘ ,(546.499;ˆ&TSUZ\s\ÿá-VSV[\ŒÿSŘ 4YTW]\®ŽÿÐ*Áœ;[UY^]àÿáB\VZ`^)ÿwáI\X\ab™ÿîâN\Y^bhGÿáT[Z_cnÿ©ß&Y[\`dx Žÿ,±®®«¤ ˜”‡}wnkc`[WSQKGE@<93.+&$ ¶,[Z]beŽÿ ¤¹ËÐÓÔÕר׀ØÙÚÛÚÛÜÜ€Ý7ÜÝÙØØ××ÔÒÒÓÏÊÅÃÁ¼¹µ²°ª¦¡›˜‘Œˆ}xqmfb\UQJF@96/*%  3\Z^dg¬ªŽÿž¹ÅÈÈÉËËÌÍËËÉÈÈÉÊËÊËÌËË€ÌÍÏÐÒÓÓÔÔÕרØÛÝÞßàáâãå€çèê€ì2ííîðïðïïîîíìëéçåâÞÛÖÓÏÌÈÄÁ½·µ°©¦ š—ˆ‚tbO<#7]Z_fhÚÿ“¼ÅÇÈÉ€ËÉ€ÈÉÉËÊÌÌË€Ì ÍÍÌÌÍÎÏÐÑÒÓÕÔ€ÕÖÕÖÖר׀ØÚÛÝ€Þ ßßàáââãä忀ç ééìîïððòóòôö€÷ööõðæÔ¶•_$<\Zagjÿ¾Ÿ¾ÇÇÈ€ÉÈÈÇÆÇÇÊËÉËÌÍÎÍÌÍÍÎÐÏÐÐÑÒÓÔÕÔÕÖÖ×€Ø×ØÙØÚÛÜÜ€Þßàá€â€äååæåææçè€éèéé€èççåßÓÀ¤…RD\\bin‡ÿC¢¾ÄÆÇÈÈÉÉÈÈÆÆÇÆÈÈÉƒÊ ËÌÍÍÎÍÍÎÐÐÒÑÓÒÒÓÔÖÖ××ØÙ‚Û ÝÝÞßÞÞßàáââ€ã€ä€åççæ‚èçèéèçäÞÑ»Ÿ€KJ\]djs+ÿü¦¿ÃÄÅÆ€Ç€ÉÈÈÇÇ‚ÊÌËÌÍÍÎÏ€Ð#ÑÑÒÒÓÔÕÔÕÔÔÕÕרÙÙØÙØÚÚÛÛÜÛÝßÞÞßßààáâã€äæ€çèé‚èççæãÜϹ|CR\_elz5Žÿ̪ÁÄÅÇÈÉ€È ÇÇÆÇÆÇÊÊËËÊÊ€ËÌËË€ÌÍÍÎÎÏÐÑ€ÐÑÒ‚ÔÕÖ××€ØÙØÙ€ÚÛÜÜÝÞÞß߀àáâããääååææççèéèçèéè€æãÛη›w]\ei{jE„‰“–™œŸ¢¤§¨«®°²³µ¸¹»»¾¿ÀÀÁÃÃÄÄÅÅÄÄÅÄÅÇÈÉÉÊÊÌÏÔØÙÜÜÞÝÓÔÕÖÖ‚× ÙÚÚÛÜÝÞÞßßÞ߀àáâ€ãâãâáàÞÖǯ‘tFC[^fh{s*w~ƒ†ˆ‹’’“–—™œž ¢¤¦§ª¬®²´·¸º»¼½¾¿ÁÁÃÅÇÉÉ€Ê€Ë ÌÍÎÏÏÒÒÓÑÑ€ÒÓÕÕÖÖ×Ö×רØÙÛÛÜÝÞßÞßà‚áàáààÜÔÅ«Œn?J\`firy;6^}xwy{~€‚„…†‡‰Š‹’”–—˜š›œž ¡£¥§©¬®±´¶¸º¼¿ÂÃÅÆÈÉËËÍÌÍ΀ÏÑÑÒÒÔÖÖ×רÚÙÚÛ€Ü ÝÝÜßßÞàáàáâ€áààÚÑÀ¥‡g7Q]bfjsLUB$ Œ _|~|zyx6zz|~ƒ„…†ˆ‰‹ŒŽ’”–—˜š›Ÿ ¢¤¦©¬®±³µ·¸º½¿ÂÅÈÈÉËËÌÎÐÒÓÓ€ÔÕÖ×רÙÚÛÛ€Ü$ÝÞßààßßàßàáàÞÙͺ€b0W]bfkoANunga]N<ˆIks{€„†€‡>†„‚€€€€‚ƒ…†‡ˆŠ‹ŒŽ’””–˜šœŸ ¡£¤¥§©¬®±³µ¶·º¼¿ÁÃÅÈÊÌÎÏÑÓÔÕ×€ÙÚÛÜÜÝÝÞÞ€ßÞßßÝÔÆ±“w[(``fkur-§ªŸ•Š~uppeE† $*>>ALY`jqy…ŠŽ€“’‹ˆ‡†…0‡ˆŠ‹ŒŽ‘“”•—–˜š›ž ¡£¤¥§©ª¬®°²µ¶¸»½¿ÂÅÈÊÌÏÐÑÓÕÖר€Ù!ÚÛÜÜÝÙÍ»¤‡mU&nhhfk[$m„™¬·º¶¯¨¨£vŠ`   )/7=HR]fpz‚ˆ“˜›œœš˜–”‘Œ‹‹Ž‘’“””–˜™›œž ¡¢¤¦§¨ª¬®¯±´·¹»¾ÀÁÃÄÄÇÈÊËÌÍÏÑÓÓÌ¿­”|fU)"€&Fvq`VT]nƒœ¹éÿ£‹‰   &,5?IV`kv€‰‘™Ÿ£¦¨§¦¤¡ž›™–”€’@“”•–—™šœœž¡£¤¥§©«¬®¯²³µ¶¸º»½¿ÀÁÂÄÅü¯‡rkK!&*,/o]Rk~‰Œ€paWVg—£! %-7BM\iw…™Ÿ¦«¯°±°­«¨¥¢Ÿœ›Ÿ ¢£¥¦©ª«­¯°°²´µ¶·¹º·®£’u{//€0<;N™¢©¬¥ƒqZ²I (4@L[jx‡“œ¥­³¸¹»»¹·µ±®¬ª¨§¦¦§¨ªª¬­¯°±³³°§Ž…ŽD-4456:,"*.//#*;0>Q`W9½0 #-7@KYhv„‘©²¹ÀÄÇÈÇÆÄÁ¾»¹·µµ´°¨¡ šI)::;>F)€ Î% !)2¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¬$+$ 1C­ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿž/" 3Ni×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ袃dJ9-%  1Kgûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷âÁ›rP5' -Fe€´ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÙµ…M+((?[z‘±Òãëïüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò50"7Pl†œ­¼ÇÌâ÷ú÷æàãæéìïòõøûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáTD-C\tŠ­¸ÀÅËÎÏÏÏÏÎÍÍÎÏÑÓÕØÛÞáåéíðô÷úýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìoZ #4I_s…•¢«²¸¼¾ÀÀÀ¿¿¿ÀÁÂÃÅÆÇÈÉÊËÍÎÐÒÔ×ÚÝàäèëïòõøûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý¿~m&7HYjy†‘™¡¦©ªªª©¨©ª«­¯±³µ¶¸º¼½¿ÁÂÄÅÆÈÉÊËÌÍÏÐÒÕØÛÞâæéíñôøûýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôòõ÷úüýýíÛÝÞÝÕ¿ŸŽw &3@N[hs|„ŠŽŽ’”—™œž £¥§©«®°²´¶¸º»½¿ÀÂÃÄÆÇÈÉÊËÌÎÐÒÕØÛßãçëïó÷úüþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÛÙ×ÖÖ××ÕÏÍÊÇü²¥’{ !+5@JT\dimoonmllnoruxz}€ƒ…ˆ‹Ž“–˜› ¢¤¦¨«¬®°²´¶¸º¼½¿ÁÃÄÅÇÈÉÊÌÍÏÑÔÖÚÝáåéíñõøûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýîØÕÒÎËÉÇÅÅÄÃÁ¿¼¶¬žŒv !)18@FKMOONMLLMOQTVY\_adgjmpsvy|‚…‡Š’”—™›ž ¢¥§©«®°²´·¸»¼¾ÀÂÃÅÆÇÉÊËÌÎÐÒÕØÜßãèìïóöùüþÿÿÿÿÿÿÿþû÷òíæáÙÑÎÉþº·´²±°¯­«¨£šŽ}i #(-134443223468:@BEGILNQSVY\^adgjmpswz}€ƒ†‰Œ’•˜š ¢¥§©¬®°³µ·¹»½¾ÀÂÅÇÉÑäéçÞÓÒÐÍÊÇÄÀ¼·°¨Ÿ–†}zxwurokg`XM@  !#%&(*,.02468:<>ACEHKMPSVY\_behlorux{‚…ˆ‹Ž‘”—™œŸ¡¤¦¨«®±µ¹½¿ÃÅÇÇÅÁ¼¸´¯«¦Ÿ—ƒxnf`\YWVTQOLHC=5,  "#%')+,.1357:6/(!   "$&'*,.0248@A@<72-(#   #&))(%"    squeezelite-1.9.9-1395+git20220104.874e4f9/rsc/jiveapp.icns000066400000000000000000000466031416534420200222100ustar00rootroot00000000000000icnsMƒh8mk ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿich#Hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿich4ˆÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÝÍÝÝÝÝÕÝÝÝÝÜÝÝÜÝÍÍÍÍÍÜÝÍÝÝÝÝÝÝÝÛÝÝÝÝÜÝÝÜÝÝÍÜÝÍÍÝÝÝÝÝÝÝÝ]ÝÝÝÝÝÝÍÍÜÜÝÍÜÝÝÍÍÝÍÝÝÝÝÝÝÜÝÝÜÜÜÝÍÍÜÜÜÜÜÝÜÝÝÜÝÝÝÝÝÝÜÝÍÝÍÜÜÜÍÍÍÍÍÜÍÍÝÝÝÝÝÝÝÝÝÜÝÍÍÍÍÍÍÍÍÍÍÍÝÝÍÝÝÝÝÝÝÜÝÍÜÜÜÜÜÜÜÜÍÍÜÜÜÜÝÍÝÝÝÝÝÝÜÜÜÜÜÍÌÌÌÌÜÌÍÍÍÍÜÝÜÝÝÝÝÍÍÝÍÍÍÌÜÌÜÜÌÍÍÍÍÍÍÍÝÝÝÝÝÝÜÜÜÜÌÌÌÜÌÌÍÌÌÌÜÜÝÜÝÝÝÝÝÍÍÍÍÍÍÍÌÌÌÌÌÌÍÍÍÍÍÍÜÝÝÝÜÜÝÍÌÌÌÌÌÌÌÌÌÌÌÌÌÜÜÝÍÝÍÝÝÜÜÜÍÌÜÌÌÌÌÌÌÌÌÌÜÜÜÜÜÝÝÍÍÍÍÍÌÜÌÌÌÌÌÌÌÌÌÌÌÌÜÜÝÍÝÝÝÍÍÌÌÌÌÌÌÌÀÌÌÌÌÌÌÜÍÍÜÜÝÝÜÝÍÍÍÌÌÌÌÌÌÀÌÌÌÌÌÍÍÍÍÝÝÝÜÜÜÌÌÌÌÌÀÌÀÌÌÌÌÌÌÌÌÜÜÜÜÝÜÜÜÌÜÌÌÌÌÀÌÌ ÌÌÌÌÍÍÍÍÍÝÝÜÜÜÜÌÌÌÀÌ ÀÀÌ ÌÌÌÌÌÍÍÝÍÝÜÜÌÌÌÌÌÌÀÌ ÀÌÌÌÌÌÜÜÜÜÝÝÜÝÍÍÌÌÌÌ ÀÌ Ì ÌÌÌÌÍÍÍÜÝÜÜÌÌÌÌÀÀÀ ÌÌ ÌÌÌÌÍÌÜÝÍÝÍÍÌÜÌÌÌÌÌÌÌÌ ÀÌÌÌÌÌÍÍÜÝÝÜÜÜÌÌÌÌÀÀÌÐÌÍÍÍÝÝÝÜÜÍÜÝÜÜÌÍÌÌÌ ÌÌÌÌÌÌÌÍÍÍÍÝÍÝÜÜÍÌÌÌÌÌÀÌÌÀÀÌÌÌÌÌÌÌÜÜÝÝÜÜÜÌÌÌÌ ÀÌ ÀÌÌÌÌÍÍÍÜÝÜÜÌÍÌÌÌÌÀÀÀÀÌ ÀÌÀÌÌÜÝÍÝÜÜÜÜÌÌÌÌ ÀÀÀÌÌÌÐ ÍÜÝÝÍÍÌÌÌÌÌÌÌÀÀ ÌÌ ÌÌ ÍÍÍÝÝÍÌÜÜÌÌÌÌÀ À ÌÀ ÝÍÝÝÜÜÜÌÌÌÌÌÌÌÌÌÀ ÌÌÌÜÝÍÝÍÍÍÌÜÜÌÌÌÀÀ ÌÌÀÌÌÍÍÍÍÝÍÝÍÍÌÌÌÌÌÌÌ ÌÀ ÌÜÍÍÜÝÝÜÝÍÍÍÌÌÌÌÀÀÌÀÌ À ÝÍÝÝÜÜÜÜÌÍÌÌÌ Ì ÌÌ ÜÌÌÍÍÝÝÝÝÝÍÍÍÌÜÜÌÀÌÌÌÌÌÜÜÝÍÍÝÝÜÜÜÜÜÌÌÌÌÌÌÌÌÌÌÍÍÍÍÍÝÝÝÝÍÍÍÍÍÌÍÌÀÌÜÌÜÌÌÜÜÝÍÝÜÝÝÝÝÝÍÍÍÍÌÍÌÌÜÌÌÜÜÍÜÝÍÝÝÝÝÜÜÜÜÜÜÜÀ ÌÜÌÍÍÍÍÝÍÍÝÝÝÝÝÝÍÝÜÜÍÍÌÌÌÌÜÜÜÜÜÜÜÝÝÝÝÝÝÝÝÍÍÍÜÍÍÍÍÍÍÌÜÜÝÍÝÝÝÝÝÝÝÝÝÍÜÝÍÜÍÍÍÍÍÍÜÜÜÝÍÜÝÝÝÝÝÝÝÝÍÜÝÍÝÍÍÍÍÍÍÝÍÍÝÝÝÝÝÞÝÝÝÝÝÍÍÜÜÝÍÜÝÍÜÜÝÝÝÝÝÝÝÚÝÝÝÝÝÝÜÝÍÜÝÍÜÝÍÝÝÝÝÝÝÝÝÝich8 úúúúùùùùùùùùùùùùùùùùVùùVùùùùùùùùùùùùùùùúúúúúúúùùùùùùùùùùùùVVVVVVVVVVVVVVùùùùùùùùùùùùùúúúúúúùùùùùùùùùVVVVVVVVVVVVVVVVVVVVùùùùùùùùùùùúúúúùùùùùùùùùùVVVVVVVVVVVVVVVVVVVVVVVùùùùùùùùùùúúúúùùùùùùùùVVVVVVVVVVøVøøøVøVVVVVVVVVVùùùùùùùùùúúúùùùùùùVVVVVVVVVøøøVVøøøøøøøøVVVVVVVVùùùùùùùùùúúúùùùùùVVVVVVVøøVVøøøøøøøøøøøøøøVVVVVVVVùùùùùùùúúúùùùùVVVVVVøøVVøøøøøøøøøøøøøøøøøVøVVVVVVùùùùùùùúúùùùVVVVVVVVVVøøøøøøøøøøøøøøøøøøøVøVVVVVVùùùùùùùúùùVVVVVVVVVøøøøøøøøøøøøøøøøøøøøøøVøVVVVVùùùùùùùùùùVVVVVVVøøøøøøøøøøøøøøøøøøøøøøøøøøøVVVVVùùùùùùùùVVVVVVVVøøøøøøøø÷÷÷÷÷÷÷øøøøøøøøøøøVøVVVVVùùùùùùVVVVVVVVøøøøøøø÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøøøøVVVVùùùùùùVVVVVøVøøøøøøø÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøøVøVVVVùùùùùVVVVVVVøøøøø÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷÷øøøøøøøVVøVVVVùùùùVVVVVøøøøøøø÷÷÷÷÷÷÷+÷++÷÷÷÷÷÷÷÷øøøøøøøVøVVVVùùùùVVVøVVøøøøø÷÷÷÷÷÷+++++++++÷÷÷÷÷÷øøøøøøVVVVVVùùùùVVVVVøøøøø÷÷÷÷÷÷++++++++++++÷÷÷÷÷øøøøøVVøVVVVùùùVVVVVøøøø÷÷÷÷÷÷++++++++++++++÷÷÷÷øøøøøøVVVVVVùùùVVVøø÷øøø÷÷÷÷÷+++++++++++++++÷÷÷÷÷øøøøøøVøVVVùùùVVøVøøøøø÷÷÷÷+++++++ö+ö+++++++÷÷÷÷øøøøøVVVVVVVùùVVVVøøøø÷÷÷÷÷+++++öö+ö+öö+++++÷÷÷÷ø÷øøøVøøVVVVùùVVøV÷øøø÷÷÷÷++++++ö++++ö+++++++÷÷÷÷øøøøøVVVVVVùùVVVV÷øøø÷÷÷÷+++++ö+øøøø+ö++++++÷÷÷øøøø÷øVVVVVVùùVVVøøøøø÷÷÷÷+++++ö+øõõø+öVøVøVVøùVùùùùùVVVVVVVùùVVVV÷øøø÷÷÷÷+++++ö+ø++ø+ö+÷÷ø÷÷øV÷øøøøøVøVVVVVùùVVVV÷øøø÷÷÷÷++++++ö+øø+ööö+++++÷÷÷÷÷ø÷øøVVVVVVùùVVVø÷ø÷ø÷÷÷÷++++++ö+ö+ööö++++++÷÷+ööö+÷VøVVVVVùùVVøVø÷øøø÷÷÷÷++++++öö+ö+++++++÷÷öõöööõöøVVVVVVùùVVVøøøøøø÷÷÷÷+++++öõööõö+õö+++÷+õöø÷÷öõ÷øVVVVùùùVVVVø÷øøø÷÷÷÷÷++++õõööõõ+öõö+÷÷öõö÷÷÷öõöVVVVVVùùVVVVøø÷øøø÷÷÷÷÷+++öõ+öõö+öõõ+÷÷õõõõõõõõöVVVVVùùùVVVøVø÷ø÷øø÷÷÷÷÷++õõ+öõö++õõ+÷+õõö÷÷÷÷÷øVVVVVùùùVVVVVøøø÷ø÷÷÷÷÷÷÷÷öõ++õö+÷öõö÷+õõöø÷øVVVVVVVùùùùVVVVVVø÷øøøø÷÷÷÷÷÷õõ++õö÷÷+õö÷öõõõ+÷øø+VVVVVùùùùVVVVVøVøø÷øøø÷÷÷÷÷öõ++õö÷÷+õõ+õ++õõõöõõøVVVVùùùùVVVVVVøVø÷øøøø÷÷÷÷õõ++õö÷÷÷öõõõ÷ø÷++++øVVVVùùùùùùVVVVVVVøøø÷øøøøø÷öõ++õö÷÷ø+õõöø÷øVøVVVVVVVùùùùùùVVVVVVVVø÷ø÷ø÷øøøõõ+÷++÷øøø+ö+÷øVøVVVVVVVùùùùùùùùVVVVVVVøVø÷øøøø÷öõ÷ø÷øø÷ø÷øø÷øVøVVVVVVVùùùùùùúùùùVVVVVVVVøøø÷÷ø÷õõ÷÷øøøøøø÷÷VVøVVVVVVVùùùùùùùúùùùùVVVVVVVVøVøø÷õõö÷ø÷÷ø÷÷øøVVøVVVVVVVVùùùùùùúúùùùùVVVVVVVVVVøVø++øøøøøøøVøVVøVVVVVVVVùùùùùùúúúùùùùùVVVVVVVVVVVVøVøVVøVøVøVøVVVVVVVVùùùùùùùúúúúùùùùùùùVVVVVVVVVVVVøVøVøVVVVVVVVVVVVùùùùùùùùúúúúùùùùùùùùVVVVVVVVVVVVVVVVVVVVVVVVVVVùùùùùùùùúúúúúùùùùùùùùùVVVVVVVVVVVVVVVVVVVVVVVùùùùùùùùùúúúúúúùùùùùùùùùùVVVVVVVVVVVVVVVVVVVùùùùùùùùùùúúúúúicl4ÝÝÝÝÝÍÝÍÝÝÝÝÝÝÝëÝÝÍÜÝÝÍÜÜÝÍÝÝÝÝÕÝÝÝÝÜÜÜÝÍÜÝÍÝÝÝÝÝÝÝÍÍÍÜÜÍÍÜÝÝÝÝÝÝÝÍÜÝÜÍÍÝÍÍÜÜÝÝÝÝÍÜÍÌÌÜÌÌÍÍÍÝÜÝÝÝÝÍÝÍÍÌÍÌÜÜÜÜÝÜÝÝÍÍÌÌÌÌÌÌÌÌÜÝÍÝÝÍÜÜÌÜÌÌÌÌÌÜÜÜÝÝÝÝÍÌÜÌÌÌÌÌÌÌÌÜÜÝÝÜÜÜÌÌÌÌÌÌÌÌÜÜÝÍÝÜÜÍÌÌÌ ÌÌÌÌÌÜÜÝÜÜÌÌÌÌÌÌÀÌÌÍÍÍÝÝÜÜÜÌÌ ÀÌÌÌÜÜÝÜÜÌÌÌÌÌ ÌÌÌÜÍÍÍÜÜÌÌÀÀÌÌÌ ÌÌÌÜÝÝÜÌÜÌÌÀÀÌ ÜÜÝÝÍÜÝÜÜÌÌÀÀÌÌÌ ÌÌÌÍÍÍÜÍÌÌÌÌÌ ÌÌ ÜÝÝÜÜÍÌÌÀÀÀÀÌÌ ÀÍÍÍÜÜÌÌÌÌÀÀÌÀÀÀÌÝÝÍÍÌÌÌÌ À ÀÌÌÍÜÝÝÍÍÌÌÌ ÀÌ ÌÍÍÍÝÍÍÌÍÌÌ Ì ÀÜÝÝÝÍÍÌÌÌ ÌÀÌÌÌÜÝÝÜÝÍÌÜÍ ÀÌÀÌÍÍÍÝÝÝÜÜÜÌÌ ÍÍÌÜÜÝÝÝÝÝÍÍÍÍÌ ÌÌÍÌÝÍÍÝÝÝÝÜÝÌÜÍÌÜÜÝÜÝÝÝÝÝÝÍÜÝÜÜÍÍÍÍÍÍÝÝÝÝÝÝÍÍÍÜÝÍÜÜÝÝÝÝÝÝÝÝÝÜÝÍÜÝÍÝÝÝÝÝÝicl8úúúùùùùùùùùVVVVVùVùùùùùùùùúúúúúùùùùùùùVVVVVVVVVVVVVùùùùùùúúúùùùùùùVVVVVVVVVVVVVVVVVùùùùùùúúùùùùVVVVVVVVVVVVVVVVVVVVùùùùùùúúùùùVVVVVVVVVøøøøVVVVVVVVVùùùùùúúùùVVVVVVVøøø÷ø÷ø÷øøøVVVVVVùùùùùúùVVVVVVVøøøøøøø÷ø÷øøøVVVVVVùùùùúùVVVVVøø÷øø÷÷÷÷÷÷ø÷øøøøVVVVVùùùùVVVVVøøøø÷÷÷÷÷+÷+÷÷÷øøøVVVVVùùùùVVVVV÷ø÷÷÷÷÷÷÷÷÷÷÷+÷÷÷÷øVVVVVùùùVVVVøøø÷÷÷÷÷+++++÷÷÷÷÷øøøVVVVùùùVVVø÷ø÷÷÷÷+++++++++÷÷÷÷÷øVVVVVùùVVVø÷ø÷÷÷++++++++++÷+÷÷øøøVVVVùùVVV÷ø÷÷÷÷+++öööö++++÷÷÷÷÷øVVVVùùVVV÷ø÷÷÷+++ööööööö+++÷+÷øøVVVVVùVVø÷ø÷÷÷+++ö+÷÷+ö++++÷÷ø÷÷VVVVùùVVøø÷÷÷÷++öö+++++øVVVVVVùVVVVVVùVVø÷ø÷÷÷++öö+÷÷+öö++++÷÷÷øVVVVùùVVV÷ø÷÷÷+++ööööööö++++öõö+VVVVVùVVV÷ø÷÷÷++++öööööö++÷öö÷+õ÷VVVùùVVV÷øø÷÷÷+++õööõ+õö++õö++õ+VVVùùVVVø÷÷÷÷÷+++õööõ+õö÷+õö+++÷VVVùùVVVVøø÷÷÷÷÷+õööõ++õ+öõ+÷øVVVVùùùVVVøø÷ø÷÷÷÷÷õööõ÷+õ+õ+õö+õVVVùùùVVVVVø÷ø÷÷÷÷õööõ÷÷öõöø÷++øVVVùùùùVVVVVø÷ø÷ø÷õ++ö÷÷+ö+÷VVVVVVùùùùùVVVVVVøø÷÷÷õ+÷øø÷÷÷øøVVVVVùùùùùùùVVVVVVVøø+õ+÷÷÷÷øøVVVVVVùùùùùúùùùVVVVVVVV÷÷øøøøVøVVVVVVùùùùùúúùùùùVVVVVVVVVVVVVVVVVVVVùùùùùúúúùùùùùVVVVVVVVVVVVVVVVVVùùùùùùúúúùùùùùùùVVVVVVVVVVVVVùùùùùùùúúúICN#ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿics#Hÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿics4ˆÝÝÝÜÝÝÝÝÝÝÍÍÜÝÝÝÝÜÜÜÍÜÝÝÜÜÌÌÌÍÍÝÝÍÌÌÌÌÝÍÜÌÌÌÌÌÜÝÍÌÌÀÌÌÌÝÜÌÀÀÌÌÍÝÍÌÌÌÌÍÍÍÜÌÌ ÀÌÝÜÜÌÀÌ ÍÜÌÌ ÀÌÌÝÜÜÌÀÌÌÍÝÝÍÌÌÌÜÝÝÝÍÍÌÜÜÝÝÝÝÜÝÍÝÝÝics8úùùùùVVVVVùùùùùùùVVVVVVVVVùùùúùVVVøøøøøøVVVùùúùVVøøøøøøøøøVVùùVVøø÷÷÷÷÷÷øøøVùùVøø÷÷÷++÷÷÷øøVVùVøø÷÷+ööö+÷øøVVùVøø÷+öööö+÷÷øøVùVø÷÷+ö+÷+÷øøøVVùVøø÷+öööö++ööøVùVøø÷÷+öööööööøVùVVøø÷÷öö+ööö÷VVùVVøøø÷öö÷ö÷øøVùùùVVøø÷öøøøøVVùùùùùVVVøøøøVVVùùùúùùùùVVVVVVVùùùúih32¸€w}€€„…†Š‚‘Ї„†…€€}€wth€fwx~€€ƒ…‚†‡Œ’•ˆ–•’‡‚†…ƒ€€xwwthffx~€€„‚†ˆ•‚–——€˜——‚–•‰‚† …€€xwwthf~€…†‡•€–—˜˜†™˜˜—€–•‡† …€xwwrf€…†‹”€–˜˜™››€››™˜˜€–•† …€xwwp…†€–˜˜™™šŸ† Ÿš€™˜€–‡€†…€€w…†‘––—˜™™›žŒ  Ÿ›™™˜—––’‡€†…€€}ww†‘––—™™šž ¥ª­±²­ª¦ Ÿš™™—––’†…€€{w€† ‘––—™™œŸ  ¢«°ˆ²±«£€ ™™˜––’†ƒ€x††––—™™žŸ ¡ª±Œ² ±ª¡  ž™™˜––€†…€}†‹•–—™™ž  £¯² °¤  ž™™—––Œ€† …€€‡”–—˜™ž  ¤±ƒ²µ¸»€¾»¹µƒ² ±¥  ž™˜—–”ˆ€† ‚€––˜™œ  ¤±‚²·½†À¾·‚² ±¥  œ™˜––€† …€––—™šŸ ¤±²µ½ŠÀ¾µ² ±¥ Ÿ›™˜––Š€†‚––˜™Ÿ ¡¯²·¿ŒÀ¿·²°¡ Ÿ™™––’€†„–˜™›  ª²·ƒÀÁÄÆÈÆÅ‚À¿·²«  œ™˜–•ˆ€†–˜™Ÿ £²µ¿ÀÂÈË‚ÌËÈÂÀ¶€²±¤ Ÿ™™––Ž€†—™š  ¬²¾ÀňÌËÆÀ¾³€²¬  ›™—–“€†˜™ ¡±€²¹ÀÆËÌÌÎÒÕÖÕÒÎÌÌËÆÀ¹€²±¢ ™˜–•ˆ††™™Ÿ ¦²¾€ÀÂËÌÌÒÖ‚×ÖÒÌÌËÀÀ¿³€²¨ Ÿ™™––Œ††™šŸ «€²¶ÀÉÌÌÒ×רÚÜÚÙ××ÓÌÌÉÀ·€²­  š™—–††™›  ¯€²º€ÀÂËÌÏ××ÚÝÞÞÝÝÚ××ÐÌËÀÀ»€²±  œ™—–‘††™œ ¡±€²½€ÀÆÌÌÔ×ÙÞÕÅÄÔÞÝÙ×ÕÌÌÇ€À½€² ±¢ œ™—–”††™ ¢²À'ÉÌÌ××ÜØ··¶¶×ÝØÔÔÍÊɾÀ½±¬¯°²¤ ™˜–”††™ ¢²À'ÊÌÎ××ÝЭìíªÏÞ¥ª™žš–Ž•ŒŽ‹‡¤ ž™˜–”††™ ¢²À'ÊÌÍ××ÝÔ¯ÉʬÓÝÆÄ³´½¾°¦²¯¡££¦¤ ž™˜–”††™ ¢²¿€ÀÈÌÌÖ×ÛÜʵ´ÈÝÝÛ×ÖÌÌÉ€À¿³€² ¤ œ™˜–”††™œ ¡±€²¼€À(ÅÌÌÓרÜÝÙÙÝÝÜØ×ÓÌÌÆÀÁÖåêæÔ¶¡ œ™—–“††™›  ¯€²¹€ÀÂËÌÍÖר܀ÝÜØ×ÖÎÌËÃÀáðèÞæð妠›™—–††™™Ÿ ª€²µ¿€À'ÇÌÌÏÖçëÝàëåØèèÕÌÈÀÎñã·²³ßñŠ𙗖ކ†˜™Ÿ ¥²½€À(ÁÊÌÌÏíñÝàñêÖêñâËÁÀÜñÚÁÀ¿Ôñן™˜––‹††˜™œ  °€²¶ÀÂÊÌÌìñÙßñèÌÝñìÄÀÀê„ñÜ™˜–•‡††—™šŸ ¨²»ÀÂÊËëñÕÚñçÌÐððËÀÉññÚ€ÅÀ¼±š™—–’€†–˜™ž  °€²³¾‚ÀÄêñÕÚñçÉÄæñÚÀÙññÚ²²±¡ ž™˜––Œ€†–—™šŸ §²´¾‚ÀêñÐÖñäÀÀÔñèÁèìðïÉ´ª«È§™—–•‡€†––˜™  ¬²´¾ÀêñÍÒñäÀÀÅîïÏðÔÏïðìêî滛––Ž€† „”–—™™Ÿ ¡¯‚² º¿ÀÀêñÌÒñä€Àâñë𿲼ÎÒÒǪ™—–•‡††…––˜™›Ÿ ¢¯‚²´¹¾êñÌÒñäÀ¿ºÌññè²²°£ Ÿ›™˜––Ž€† „€†“––˜™œ  ¢¯„²éñÇÂØÏ´²²¶ÜÞɲ¯£ Ÿœ™˜––“€† …€†ˆ•–—™™œ€ «ƒ²éñDz ¬¡  œ™™—––‰€†„€€††‹•–—™™›Ÿ  ¤®€²µìñ¿…²¯¥€ œ™™—––Œ€†…€€{€†Ž––—™™šŸ€ ¥¬¶îðæ´‚²±¬¦€ Ÿš™˜—––†‚€~w†Ž€–˜™™œŸ€  ¥Îǰ®¯®ª©¥¡€ Ÿ™™˜€–†ƒ€yw„†•––˜€™œŸŠ Ÿ€™˜––•ކ„€€{ww€…†‹”––—˜€™šžŸ‚ Ÿž›€™˜—––”Œ† „€€|wwv€€…†ˆ‘•––—˜˜™‚š™˜˜—€–‘‰† „€€~wwvl}€€„‚†Š“–—€˜‚™€˜—–“‹‚† „€€~wwvofw}€€ƒƒ†‹’Ž–’‹ƒ†ƒ€€~€wrff€w}€€„…†Š‚‘Ї„†…€€}€wth€fwx~€€ƒ…‚†‡Œ’•ˆ–•’‡‚†…ƒ€€xwwthffx~€€„‚†ˆ•‚–——€˜——‚–•‰‚† …€€xwwthf~€…†‡•€–—˜˜†™˜˜—€–•‡† …€xwwrf€…†‹”€–˜˜™››€››™˜˜€–•† …€xwwp…†€–˜˜™™šŸ† Ÿš€™˜€–‡€†…€€w…†‘––—˜™™›žŒ  Ÿ›™™˜—––’‡€†…€€}ww†‘––—™™šž ¥ª­±²­ª¦ Ÿš™™—––’†…€€{w€† ‘––—™™œŸ  ¢«°ˆ²±«£€ ™™˜––’†ƒ€x††––—™™žŸ ¡ª±Œ² ±ª¡  ž™™˜––€†…€}†‹•–—™™ž  £¯² °¤  ž™™—––Œ€† …€€‡”–—˜™ž  ¤±ƒ²µ¸»€¾»¹µƒ² ±¥  ž™˜—–”ˆ€† ‚€––˜™œ  ¤±‚²·½†À¾·‚² ±¥  œ™˜––€† …€––—™šŸ ¤±²µ½ŠÀ¾µ² ±¥ Ÿ›™˜––Š€†‚––˜™Ÿ ¡¯²·¿ŒÀ¿·²°¡ Ÿ™™––’€†„–˜™›  ª²·ƒÀÁÄÆÈÆÅ‚À¿·²«  œ™˜–•ˆ€†–˜™Ÿ £²µ¿ÀÂÈË‚ÌËÈÂÀ¶€²±¤ Ÿ™™––Ž€†—™š  ¬²¾ÀňÌËÆÀ¾³€²¬  ›™—–“€†˜™ ¡±€²¹ÀÆËÌÌÎÒÕÖÕÒÎÌÌËÆÀ¹€²±¢ ™˜–•ˆ††™™Ÿ ¦²¾€ÀÂËÌÌÒÖ‚×ÖÒÌÌËÀÀ¿³€²¨ Ÿ™™––Œ††™šŸ «€²¶ÀÉÌÌÒ×רÚÜÚÙ××ÓÌÌÉÀ·€²­  š™—–††™›  ¯€²º€ÀÂËÌÏ××ÚÝÞÞÝÝÚ××ÐÌËÀÀ»€²±  œ™—–‘††™œ ¡±€²½€ÀÆÌÌÔ×ÙÞÕÅÄÔÞÝÙ×ÕÌÌÇ€À½€² ±¢ œ™—–”††™ ¢²À'ÉÌÌ××ÜØ··¶¶×ÝØÔÔÍÊɾÀ½±¬¯°²¤ ™˜–”††™ ¢²À'ÊÌÎ××ÝЭìíªÏÞ¥ª™žš–Ž•ŒŽ‹‡¤ ž™˜–”††™ ¢²À'ÊÌÍ××ÝÔ¯ÉʬÓÝÆÄ³´½¾°¦²¯¡££¦¤ ž™˜–”††™ ¢²¿€ÀÈÌÌÖ×ÛÜʵ´ÈÝÝÛ×ÖÌÌÉ€À¿³€² ¤ œ™˜–”††™œ ¡±€²¼€À(ÅÌÌÓרÜÝÙÙÝÝÜØ×ÓÌÌÆÀÁÖåêæÔ¶¡ œ™—–“††™›  ¯€²¹€ÀÂËÌÍÖר܀ÝÜØ×ÖÎÌËÃÀáðèÞæð妠›™—–††™™Ÿ ª€²µ¿€À'ÇÌÌÏÖçëÝàëåØèèÕÌÈÀÎñã·²³ßñŠ𙗖ކ†˜™Ÿ ¥²½€À(ÁÊÌÌÏíñÝàñêÖêñâËÁÀÜñÚÁÀ¿Ôñן™˜––‹††˜™œ  °€²¶ÀÂÊÌÌìñÙßñèÌÝñìÄÀÀê„ñÜ™˜–•‡††—™šŸ ¨²»ÀÂÊËëñÕÚñçÌÐððËÀÉññÚ€ÅÀ¼±š™—–’€†–˜™ž  °€²³¾‚ÀÄêñÕÚñçÉÄæñÚÀÙññÚ²²±¡ ž™˜––Œ€†–—™šŸ §²´¾‚ÀêñÐÖñäÀÀÔñèÁèìðïÉ´ª«È§™—–•‡€†––˜™  ¬²´¾ÀêñÍÒñäÀÀÅîïÏðÔÏïðìêî滛––Ž€† „”–—™™Ÿ ¡¯‚² º¿ÀÀêñÌÒñä€Àâñë𿲼ÎÒÒǪ™—–•‡††…––˜™›Ÿ ¢¯‚²´¹¾êñÌÒñäÀ¿ºÌññè²²°£ Ÿ›™˜––Ž€† „€†“––˜™œ  ¢¯„²éñÇÂØÏ´²²¶ÜÞɲ¯£ Ÿœ™˜––“€† …€†ˆ•–—™™œ€ «ƒ²éñDz ¬¡  œ™™—––‰€†„€€††‹•–—™™›Ÿ  ¤®€²µìñ¿…²¯¥€ œ™™—––Œ€†…€€{€†Ž––—™™šŸ€ ¥¬¶îðæ´‚²±¬¦€ Ÿš™˜—––†‚€~w†Ž€–˜™™œŸ€  ¥Îǰ®¯®ª©¥¡€ Ÿ™™˜€–†ƒ€yw„†•––˜€™œŸŠ Ÿ€™˜––•ކ„€€{ww€…†‹”––—˜€™šžŸ‚ Ÿž›€™˜—––”Œ† „€€|wwv€€…†ˆ‘•––—˜˜™‚š™˜˜—€–‘‰† „€€~wwvl}€€„‚†Š“–—€˜‚™€˜—–“‹‚† „€€~wwvofw}€€ƒƒ†‹’Ž–’‹ƒ†ƒ€€~€wrff€w}€€„…†Š‚‘Ї„†…€€}€wth€fwx~€€ƒ…‚†‡Œ’•ˆ–•’‡‚†…ƒ€€xwwthffx~€€„‚†ˆ•‚–——€˜——‚–•‰‚† …€€xwwthf~€…†‡•€–—˜˜†™˜˜—€–•‡† …€xwwrf€…†‹”€–˜˜€™šœœ€Ÿœœš€™˜˜€–•† …€xwwp…†€–˜˜™™›Ÿ¢†¤¢Ÿ›€™˜€–‡€†…€€w…†‘––—˜™™œ¢Œ¤ ¢™™˜—––’‡€†…€€}ww†‘––—™™›¢¤¨«®±²®¬¨¤¢›™™—––’†…€€{w€† ‘––—™™ž£¤¤¦¬±ˆ²±­¦€¤Ÿ™™˜––’†ƒ€x††––—™™¡£¤¥¬±Œ² ±¬¥¤¤¡š™˜––€†…€}†‹•–—™™¡¤¤§¯² °§¤¤¢š™—––Œ€† …€€‡”–—˜™ ¤¤§±ƒ²µ¸»€¾»¹µƒ² ±¨¤¤¡™˜—–”ˆ€† ‚€––˜™ž¤¤§±‚²·½†À¾·‚² ±¨¤¤Ÿ™˜––€† …€––—™›£¤§±²µ½ŠÀ¾µ² ±¨¤£œ™˜––Š€†‚––˜™¢¤¥¯²·¿ŒÀ¿·²°¥¤¢™™––’€†„–˜™¤¤«²·ƒÀÁÄÆÈÆÅ‚À¿·²­¤¤ž™˜–•ˆ€†–˜™¢¤¦²µ¿ÀÂÈË‚ÌËÈÂÀ¶€²±§¤£™™––Ž€†—™œ¤¤­²¾ÀňÌËÆÀ¾³€²®¤¤™—–“€†˜™Ÿ¤¤±€²¹ÀÆËÌÌÎÒÕÖÕÒÎÌÌËÆÀ¹€²±¥¤ ™˜–•ˆ††™™£¤©²¾€ÀÂËÌÌÒÖ‚×ÖÒÌÌËÀÀ¿³€²ª¤£™™––Œ††™š£¤¬€²¶ÀÉÌÌÒ×רÚÜÚÙ××ÓÌÌÉÀ·€²®¤¤›™—–††™¤¤°€²º€ÀÂËÌÏ××ÚÝÞÞÝÝÚ××ÐÌËÀÀ»€²±¤¤™—–‘††™¤¥±€²½€ÀÆÌÌÔ×ÙÞÕÅÄÔÞÝÙ×ÕÌÌÇ€À½€² ±¥¤Ÿ™—–”††™ ¤¥²À'ÉÌÌ××ÜØ·¹·¶×ÝØÔÔÍÊɾÀ½±¬¯°²§¤ ™˜–”††™ ¤¥²À'ÊÌÎ××ÝЮìí«ÏÞ¥ª™žš–žŽ•ŒŽŒ‡§¤¡™˜–”††™ ¤¥²À'ÊÌÍ××ÝÔ°ÊË­ÓÝÆÄ³´½¾°¦²¯¡££¦§¤¡™˜–”††™Ÿ¤¥²¿€ÀÈÌÌÖ×ÛÜʵµÈÝÝÛ×ÖÌÌÉ€À¿³€² §¤Ÿ™˜–”††™¤¥±€²¼€À(ÅÌÌÓרÜÝÙÙÝÝÜØ×ÓÌÌÆÀÁÖåêæÔ·¥¤Ÿ™—–“††™œ¤¤¯€²¹€ÀÂËÌÍÖר܀ÝÜØ×ÖÎÌËÃÀáðèÞæð婤™—–††™š£¤¬€²µ¿€À'ÇÌÌÏÖçëÝàëåØèèÕÌÈÀÎñã·²³ßñƤ›™—–ކ†˜™¢¤§²½€À(ÁÊÌÌÏíñÝàñêÖêñâËÁÀÜñÚÁÀ¿ÔñØ¢™˜––‹††˜™ž¤¤°€²¶ÀÂÊÌÌìñÙßñèÌÝñìÄÀÀê„ñÝ ™˜–•‡††—™›£¤ª²»ÀÂÊËëñÕÚñçÌÐððËÀÉññÚ€ÅÁ¾´›™—–’€†–˜™¡¤¤°€²³¾‚ÀÄêñÕÚñçÉÄæñÚÀÙññÚ²²±¥¤¡™˜––Œ€†–—™œ£¤©²´¾‚ÀêñÐÖñäÀÀÔñèÁèìðïÉ´¬®É¨™—–•‡€†––˜™ ¤¤®²´¾ÀêñÍÒñäÀÀÅîïÏðÔÏïðìêî滛––Ž€† „”–—™š£¤¥°‚² º¿ÀÀêñÌÒñä€Àâñë𿲼ÏÔÓȪ™—–•‡††…––˜™œ£¤¥°‚²´¹¾êñÌÒñäÀ¿ºÌññè²²°¦¤£™˜––Ž€† „€†“––˜™¤¤¥°„²éñÇÂØÏ´²²¶ÜÞɲ¯¦¤£ž™˜––“€† …€†ˆ•–—™™Ÿ€¤­ƒ²éñDz­€¤ž™™—––‰€†„€€††‹•–—™™£¤¤§¯€²µìñ¿…²¯¨€¤ž™™—––Œ€†…€€{€†Ž––—™™›¢€¤¨­¶îðæ´‚²±­©€¤¢›™˜—––†‚€~w†Ž€–˜™™ž£€¤©Ïɲ€¯¬«¨¤£Ÿš™˜€–†ƒ€yw„†•––˜€™ž¢Š¤ £Ÿš™™˜––•ކ„€€{ww€…†‹”––—˜€™›Ÿ¢£‚¤£¢Ÿœ€™˜—––”Œ† „€€|wwv€€…†ˆ‘•––—˜˜™‚š™˜˜—€–‘‰† „€€~wwvl}€€„‚†Š“–—€˜‚™€˜—–“‹‚† „€€~wwvofw}€€ƒƒ†‹’Ž–’‹ƒ†ƒ€€~€wrffil32 Õ wx~€ƒ…†…†ˆŒ’€“‘‹††…†…‚}wwnfey…€†‹’•€–€—€–•‰€†„€~wvnf……†Š“•–—˜˜‚™;˜˜—–•‡†……€~wvk…††Ž•–˜™™œŸŸ Ÿž›™˜—–•І†…€~wv……†–—˜šŸ  ¤€¦¢  Ÿœ™˜–•Œ†…„€|v††–—™œŸ ¦¬±‚²6°ª£ Ÿš˜—•Œ††ƒy†Ž–—™Ÿ£®±²±²±²±²±²±«¡Ÿœ˜—•‰†…~‰•—™ ¥±€²³·¹º¸¶²4¯¢Ÿ›˜–”‡†…€“–˜œ ¥±±²´»¿À¿À¿À¿¹²²±°¢Ÿš˜–Ž…†‚–˜šŸ£±²²·¿†À8¾´²²¯ ž™—•‡†„–˜ ®±²·À¿ÀÁÅÈÉÇÄÀÀ¿¾´²±©Ÿ›˜–†…—™Ÿ¦²²´¿ÀÀÅË‚Ì6ÊÂÀÀ¾²²±¡ž™–“††˜œ ­²±¼¿ÀÅËÌÒÕÖÔÐÌËÂÀ¿·±²¨ š—•ˆ…™ž¡±²´¿ÀÂËÌÕ×ÙÚØ×ÓÌÉÀÀ¾²²­ œ˜–‹†™Ÿ¥±²¸À¿ÆËÒ×ÜÚÚÝÚÖÏËÿÀ´²± ˜–Ž…™Ÿ§²²ºÀÀÉÌÖÙÓ¸¶ÓÜÕÒËÅÀ¾­°²¢ž™–†™Ÿ§±²»À¿ÊÍ×ÛÆÓÓÄΩ œ–™‘¢Ÿ™–…™Ÿ§²²»ÀÀÊÌÖÚÕ¶µÔÝØÎÌÆÀÀ·±²¢ž™–†™Ÿ¥±²¸À¿ÇËÓ×ÜÛÛÝÚ×ÐËÄÆâìçÊ ˜–Ž…™ž¢±²µÀÀÂËÍÕãàáäÛâÐÊÀåÞ¼É𳜘–Œ†˜œ ®²±½¿ÀÆËÍîãæìÖðÜÃÄðÜÐÐîÅš˜•‰…—šŸ§²²µ¿ÀÀÆËíÞâêÌéåÀÔñÝÔÓÍ´™—”††–˜ž ¯±²¹À¿ÀÂìÝáéÅ×ïÄãðÒ±«¢˜–ކ…–˜šŸ¥±²²¹¿ÀÀìÙÝèÀÆðÖîÓïÝÕ褗•ˆ†…“–˜œ ¨±±²¶½¿ìØÜèÀ¿äîá±¼ÁÀª™–…†‚‹•—™ž ©±€²BµìÖÐÙ·´Çäǰ¥Ÿœ˜–”‡†…€†–˜™ž ¥¯±²±ìÒ²±²±²±­¢ œ™—–‹†…‚††’–˜™Ÿ ¨¯ÑðÅ€²;±­¥ Ÿ›˜—–ކ†„€z……‡’–—˜šžŸ ¶¶¨ª§¥¡ Ÿ™˜—–†……€}w‚…††––˜™šœžŸ€ Ÿžœ™˜˜–•Œ††…€~wv‚……†Œ”––€˜‚™˜˜—–•’‰†……€xvmz…€†”€–‚—€–’‹€†„€xwpf wx~€ƒ…†…†ˆŒ’€“‘‹††…†…‚}wwnfey…€†‹’•€–€—€–•‰€†„€~wvnf……†Š“•–—˜˜‚™;˜˜—–•‡†……€~wvk…††Ž•–˜™™œŸŸ Ÿž›™˜—–•І†…€~wv……†–—˜šŸ  ¤€¦¢  Ÿœ™˜–•Œ†…„€|v††–—™œŸ ¦¬±‚²6°ª£ Ÿš˜—•Œ††ƒy†Ž–—™Ÿ£®±²±²±²±²±²±«¡Ÿœ˜—•‰†…~‰•—™ ¥±€²³·¹º¸¶²4¯¢Ÿ›˜–”‡†…€“–˜œ ¥±±²´»¿À¿À¿À¿¹²²±°¢Ÿš˜–Ž…†‚–˜šŸ£±²²·¿†À8¾´²²¯ ž™—•‡†„–˜ ®±²·À¿ÀÁÅÈÉÇÄÀÀ¿¾´²±©Ÿ›˜–†…—™Ÿ¦²²´¿ÀÀÅË‚Ì6ÊÂÀÀ¾²²±¡ž™–“††˜œ ­²±¼¿ÀÅËÌÒÕÖÔÐÌËÂÀ¿·±²¨ š—•ˆ…™ž¡±²´¿ÀÂËÌÕ×ÙÚØ×ÓÌÉÀÀ¾²²­ œ˜–‹†™Ÿ¥±²¸À¿ÆËÒ×ÜÚÚÝÚÖÏËÿÀ´²± ˜–Ž…™Ÿ§²²ºÀÀÉÌÖÙÓ¸¶ÓÜÕÒËÅÀ¾­°²¢ž™–†™Ÿ§±²»À¿ÊÍ×ÛÆÓÓÄΩ œ–™‘¢Ÿ™–…™Ÿ§²²»ÀÀÊÌÖÚÕ¶µÔÝØÎÌÆÀÀ·±²¢ž™–†™Ÿ¥±²¸À¿ÇËÓ×ÜÛÛÝÚ×ÐËÄÆâìçÊ ˜–Ž…™ž¢±²µÀÀÂËÍÕãàáäÛâÐÊÀåÞ¼É𳜘–Œ†˜œ ®²±½¿ÀÆËÍîãæìÖðÜÃÄðÜÐÐîÅš˜•‰…—šŸ§²²µ¿ÀÀÆËíÞâêÌéåÀÔñÝÔÓÍ´™—”††–˜ž ¯±²¹À¿ÀÂìÝáéÅ×ïÄãðÒ±«¢˜–ކ…–˜šŸ¥±²²¹¿ÀÀìÙÝèÀÆðÖîÓïÝÕ褗•ˆ†…“–˜œ ¨±±²¶½¿ìØÜèÀ¿äîá±¼ÁÀª™–…†‚‹•—™ž ©±€²BµìÖÐÙ·´Çäǰ¥Ÿœ˜–”‡†…€†–˜™ž ¥¯±²±ìÒ²±²±²±­¢ œ™—–‹†…‚††’–˜™Ÿ ¨¯ÑðÅ€²;±­¥ Ÿ›˜—–ކ†„€z……‡’–—˜šžŸ ¶¶¨ª§¥¡ Ÿ™˜—–†……€}w‚…††––˜™šœžŸ€ Ÿžœ™˜˜–•Œ††…€~wv‚……†Œ”––€˜‚™˜˜—–•’‰†……€xvmz…€†”€–‚—€–’‹€†„€xwpf wx~€ƒ…†…†ˆŒ’€“‘‹††…†…‚}wwnfey…€†‹’•€–€—€–•‰€†„€~wvnf……†Š“•–—˜˜‚™;˜˜—–•‡†……€~wvk…††Ž•–˜™š ¢£¤£¢ œ™˜—–•І†…€~wv……†–—˜š £¤¤§€¨¦¤¤£ž™˜–•Œ†…„€|v††–—™ž£¤¨­±‚²6°¬¦¤¢›˜—•Œ††ƒy†Ž–—™ £¦¯±²±²±²±²±²±­¤£˜—•‰†…~‰•—™ ¤¨±€²³·¹º¸¶²4°¦£˜–”‡†…€“–˜ž¤¨±±²´»¿À¿À¿À¿¹²²±°¦£›˜–Ž…†‚–˜š£¦±²²·¿†À8¾´²²¯¤¢™—•‡†„–˜ ¤¯±²·À¿ÀÁÅÈÉÇÄÀÀ¿¾´²±«£˜–†…—𣩲²´¿ÀÀÅË‚Ì6ÊÂÀÀ¾²²±¥¢™–“††˜ž¤®²±¼¿ÀÅËÌÒÕÖÔÐÌËÂÀ¿·±²ª¤›—•ˆ…™ ¤±²´¿ÀÂËÌÕ×ÙÚØ×ÓÌÉÀÀ¾²²®¤ž˜–‹†™£§±²¸À¿ÆËÒ×ÜÚÚÝÚÖÏËÿÀ´²±¤ ˜–Ž…™£©²²ºÀÀÉÌÖÙÓ¹·ÓÜÕÒËÅÀ¾®°²¦¡™–†š£©±²»À¿ÊÍ×ÛÆÔÔÄΩ œ–™’¦¢™–…𣩲²»ÀÀÊÌÖÚÕ¶¶ÔÝØÎÌÆÀÀ·±²¦¡™–†™£¨±²¸À¿ÇËÓ×ÜÛÛÝÚ×ÐËÄÆâìçʤ ˜–Ž…™¡¥±²µÀÀÂËÍÕãàáäÛâÐÊÀåÞ¼É𵞘–Œ†˜ž¤¯²±½¿ÀÆËÍîãæìÖðÜÃÄðÜÐÐîÇ›˜•‰…—𣩲²µ¿ÀÀÆËíÞâêÌéåÀÔñÝÔÔ϶™—”††–˜¡¤°±²¹À¿ÀÂìÝáéÅ×ïÄãðÒ±¬¦Ÿ˜–ކ…–˜›£¨±²²¹¿ÀÀìÙÝèÀÆðÖîÓïÝÖ褗•ˆ†…“–˜Ÿ¤ª±±²¶½¿ìØÜèÀ¿äîᱼê™–…†‚‹•—™¡¤«±€²BµìÖÐÙ·´ÇäDZ§£ž˜–”‡†…€†–˜™¡¤¨°±²±ìÒ²±²±²±®¥¤ž™—–‹†…‚††’–˜™Ÿ£¤ª¯ÑðÅ€²;±®¨¤£˜—–ކ†„€z……‡’–—˜›¡£¤¹¹ª«ª¨¥¤£Ÿš˜—–†……€}w‚…††––˜™›Ÿ¢£€¤£¡žš˜˜–•Œ††…€~wv‚……†Œ”––€˜™€š™˜˜—–•’‰†……€xvmz…€†”€–‚—€–’‹€†„€xwpfis32z…‡’••“‰…ƒ|uh‚†•˜šœœ›™—‡„}t…—› §«¬©£˜“‡ƒ{Œ—¦±²µµ³±«Ÿ™’†–›¦±·¾ÀÀ¿º²¬ž—Œ„˜¡±·ÀÄÉÊÆÀ¼²§›“†›¨²¿ÄÎÕ×ÑÈÀ¶®—‡œ¬µÀÊÖÐÐÙθ±Ÿ—Šœ¬¶ÀËØÉÈ˶®§  —Š›ª´¿ÈÓßßÜÍÌÚÛ£—‰™¥²¼ÁÊççßÑÞר«–†—ž®³¾ÀääÈÞåÔœ…“™¡¯³¹áۺ߰§–ˆƒˆ•š ª¹Ý²±­£œ—…~…‰•˜œ¤§¤¡š–Ž…x„‡–—˜˜——’Š…ynz…‡’••“‰…ƒ|uh‚†•˜šœœ›™—‡„}t…—› §«¬©£˜“‡ƒ{Œ—¦±²µµ³±«Ÿ™’†–›¦±·¾ÀÀ¿º²¬ž—Œ„˜¡±·ÀÄÉÊÆÀ¼²§›“†›¨²¿ÄÎÕ×ÑÈÀ¶®—‡œ¬µÀÊÖÐÐÙθ±Ÿ—Šœ¬¶ÀËØÉÈ˶®§  —Š›ª´¿ÈÓßßÜÍÌÚÛ£—‰™¥²¼ÁÊççßÑÞר«–†—ž®³¾ÀääÈÞåÔœ…“™¡¯³¹áۺ߰§–ˆƒˆ•š ª¹Ý²±­£œ—…~…‰•˜œ¤§¤¡š–Ž…x„‡–—˜˜——’Š…ynuz…‡’••“‰…ƒ|uh‚†•˜œžž™—‡„}t…—¤©­­«¦Ÿ™“‡ƒ{Œ—Ÿ©±²µµ³±­¢™’†–©±·¾ÀÀ¿º²®¡—Œ„˜¤±·ÀÄÉÊÆÀ¼²©œ“†œª²¿ÄÎÕ×ÑÈÀ¶¯ —‡ž­µÀÊÖÐÐÙθ±¢—ŠŸ­¶ÀËØÉÈ˶®§ ¤—Ь´¿ÈÓßßÜÍÌÚÛ¦—‰š¨²¼ÁÊççßÑÞר¬–†—¡®³¾ÀääÈÞåÔÄ…“𥰳¹áۺ߲©–ˆƒˆ•›¤¬¹Ý²±®§ž—…~…‰•™ž§ª§¤ š–Ž…x„‡–—€˜—’Š…ynl8mkÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿs8mkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsqueezelite-1.9.9-1395+git20220104.874e4f9/rsc/jiveapp.ico000066400000000000000000000236261416534420200220260ustar00rootroot0000000000000000hf èÎ(¶ 00¨Þ ¨†h."(0`€€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwwwwwwxw‡xw‡xw‡wwwwwwwwwwwwww‡‡xxw‡xw‡xxwwwwwwwqwwww‡xw‡w‡‡‡‡‡‡w‡‡wwwwwvwww‡xw‡x‡‡‡‡‡‡xxxw‡xwwwwwww‡‡‡x‡‡‡‡‡‡ˆxxw‡wwwwwwww‡wxx‡‡ˆˆˆˆxxxxxxxwwwwwwxxxxxxxøˆxˆ‡‡‡‡w‡‡wwwwwww‡‡‡‡ˆ‡ÿˆˆxˆˆxx‡xw‡wwww‡‡‡‡‡ˆ‡ˆˆxˆxˆˆxxw‡wxwwxxxxxˆˆˆˆÿˆˆˆˆˆˆ‡‡‡‡‡wwwww‡‡‡ˆxxˆˆÿˆˆÿˆˆxxw‡‡wwxxxxˆ‡ˆˆˆøˆøˆˆÿøxˆˆ‡‡wwwxw‡‡‡ˆˆˆˆÿˆˆøÿÿøw‡www‡‡ˆˆˆˆˆˆÿˆøˆøÿÿˆx‡‡xww‡‡‡ˆxxˆˆˆøˆÿˆˆˆ‡‡‡‡w‡xxxˆˆˆˆˆˆÿˆÿˆÿˆøˆˆˆxw‡ww‡ˆxxˆˆˆˆÿøøˆÿÿÿøw‡ww‡‡ˆˆˆˆˆˆˆÿÿøˆˆøˆˆø‡‡‡wxxxxˆˆˆˆøøøˆˆÿˆø‡xwwxxˆ‡ˆˆˆˆˆøˆˆÿˆxw‡wxxxˆˆˆˆøøˆøøˆˆˆÿˆ‡‡‡xwxx‡ˆˆˆˆˆˆˆˆˆˆˆˆˆˆxxwwxxˆ‡ˆˆˆøøøˆˆˆˆˆˆˆ‡‡‡‡w‡wxxxˆˆˆˆˆÿˆ‡‡‡wwwxx‡xw‡‡ˆxˆˆˆˆˆˆˆˆøˆˆˆˆˆ‡‡xwwxxˆˆˆˆøˆˆøøˆˆˆˆ‡ˆxw‡wxw‡‡ˆˆˆˆøˆøøˆøˆˆˆˆ‡‡‡xwxxˆˆˆˆˆˆˆøøˆˆˆˆˆxxxxwwxxxxˆˆˆˆøˆøˆˆˆˆˆ‡‡w‡wxxxˆxˆˆˆˆˆˆøˆˆˆˆ‡‡‡‡‡wwxxxˆˆˆˆˆˆˆˆˆˆˆˆˆˆxxxxwxw‡‡‡ˆˆˆˆˆˆˆˆˆˆˆ‡‡‡‡wwww‡‡ˆˆˆˆˆˆˆˆˆˆˆˆˆx‡‡xxww‡‡‡‡ˆxˆˆˆˆˆˆˆˆˆˆˆˆxxw‡wwwxxx‡ˆxˆˆˆˆˆˆˆˆˆxxxxxww‡xxw‡ˆˆˆˆˆˆˆˆˆˆˆˆˆxxw‡w‡ww‡‡‡‡‡‡‡ˆˆˆˆˆˆ‡‡‡‡‡‡xwwwwwxxxxˆˆˆxˆˆ‡ˆˆˆxxwxwwwww‡‡w‡‡‡ˆxˆxxˆ‡‡‡‡‡‡‡wwwwwwxxxxx‡ˆˆˆˆxˆ‡‡‡‡xwxwwwwxw‡xxxxxxxx‡‡xxxxw‡wwwwwwwxw‡‡‡‡‡‡‡‡‡‡‡ww‡wwwwwwwxw‡w‡xxx‡‡‡‡‡x‡‡wwwwwwwxwwxxxw‡‡xxxxxwxwwxwwwwwwwwww‡‡xxw‡xww‡‡w‡wwwwwwwwwxwwxww‡xw‡‡wwwwwwwwGwwwxwwxw‡‡‡‡‡xw‡wwwwwwwswwwwwwwwwwwwwwwww‡wwwwtw( @€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwwwwxw‡xw‡wwwwwwwww‡‡‡xw‡xxwwwwwww‡xwxx‡‡‡‡‡‡wwwwwxwˆx‡ˆxxwxwwwww‡‡‡‡ˆøˆˆ‡ˆw‡‡wwwxxxˆˆø‡‡ˆxxwwwwxw‡ˆx‡øˆˆ‡‡‡www‡‡ˆˆˆøøˆˆˆˆxww‡‡ˆ‡ˆˆøÿˆøøøxwww‡‡ˆˆˆøˆøÿˆ‡‡‡w‡‡ˆˆˆˆøÿøˆˆ‡xwxxˆˆˆˆÿˆˆwwxx‡ˆˆˆˆø‡‡‡x‡ˆˆˆˆˆøøˆˆøøxwwxxˆˆˆˆˆøˆˆˆ‡‡‡xˆxˆˆˆøxxww‡xwxxˆˆˆˆˆøˆˆˆxwwxxˆˆˆˆˆøøˆˆˆx‡‡‡xxxˆˆøøøøˆˆˆxxwxxˆˆˆˆˆˆˆˆ‡‡‡wwxx‡ˆˆˆøøˆˆˆˆˆxxwxxxˆˆˆˆˆˆˆˆxxw‡ww‡ˆxˆˆˆˆˆˆˆˆxxww‡xxˆxˆˆˆˆˆxxxwwww‡‡ˆˆˆˆˆˆˆˆxw‡wwww‡w‡‡ˆ‡ˆxxxxwxww‡x‡ˆˆxˆˆ‡‡‡wxwwww‡xwx‡‡w‡‡xxwwwwww‡‡‡xx‡‡xwwwwwwwwwxxxw‡xw‡wwwwww‡xww‡xxw‡wwwwuwwwww‡w‡wwwwwwwc( €€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿwwxw‡wwww‡‡ˆxxwww‡ˆˆˆxwwxxˆˆˆ‡wxˆˆøˆˆwxxˆˆø÷‡xˆˆøøˆw‡ˆˆˆˆ‡‡‡xˆˆˆ‡w‡ˆˆˆˆˆwxˆˆˆˆˆxww‡ˆˆˆˆw‡xxˆˆˆ‡‡wwxxx‡xwwww‡‡xwwwwwwxwwww(0` fffhhhmmmqqqvvvxxx}}}€€€………‰‰‰Œ‹‹‘‘‘–––˜˜˜œššžœœ¡žž¢¢¢¤  ¦¤¤¨¥¥ª©©¬ªª®­­°¯¯±±±´±±µµµ¹··¹¹¹¾¾¾ÀÀÀÅÅÅÈÇÇÉÉÉÌÌÌÑÑÑÔÒÒÖÖÖØ××ÙÙÙÝÝÝáááåååéééíííðððÿÿÿ                                      &#     /0-     /0    -0!    /0$!(&(,$     !-0%(0,!!&00-   !!!!00%(0-!!!,0/0!!&((#  !!!!!-0&&0-!!$00&0(&/0/-00    !!!!!!00&(0-!!&0-!-/00%% !!!!!!!-0((0-$!-0+!(00+  !!!!!%&/0(+0-&&00$!%00+!!!!! !!!!!$&$-0(,0-%,0/$!!/0000000+ !!!!!%$%&00,,00(-0-$!!+0+!!!+0)  !!!!$%&&(-0+,/-(/-(%$!%0-+0! !!!!&$$(((,+(,((((&%$!!-0-+-0- !!!!$$%(((,(+(,,+((($&$!!(---( !!!!$$%(((,$$+++((%%$!!!! !!!!$&$((,&$$&,$!!  !!!!$$&((,&/0&+    !!!!$&$((+((,((&$$$!!   !!!$$&&((,($$(,((((&$$!!!   !!!$$&((++(,(,+((&$%!!!!  !$%&&(((,(,((((&$$!!!!   " "$%%(((((((((%$%"!!!!  !!!"%%$&&(((&&$&$$!!!!   !!!!"$&$&$&$&$%$$!!!!!  !!!!!!$$$%$%$%$!! !!!  !!!!!!!$"$$"!!!!!!    !!!!!!!!!!!!!!!!    !!! !!!!!   !!!! !!!!   !!                                                                      ( @eeekkkmmmpppvvvxxx~~~€€€………‰‰‰‘‘‘–––˜˜˜œ››žœœ¢žž   ¤  ¦¤¤¨¥¥ª¨¨¬ªª®­­°¯¯±±±µ³³µµµ¹¶¶¹¹¹¾¾¾ÀÀÀÅÅÅÊÊÊÌÌÌÑÑÑÔÓÓÕÕÕÙÙÙÜÜÜáááåååèèèíííðððÿÿÿ        %-!  ,% ,%%(!*# ,((,*,*!! ,((,#,%-%,(%, !,(*,!%-!*,% !##,(*,#,*%-(%%% ###,**,%-(!!-(%#,! !##%****(*##*(#- ##%(((((((%#!!*,*# ##((%%((##!! ##((#%%!#   ##%(%%(%%## ##%(((((((%#!! %#%((((%%## ###%%%%%##! ########! ####! ! !!               ( hhhnnntttyyy}}}………‰‰‰’’’–––˜——™˜˜œššžœœ¡žž   ¤¡¡§¥¥©¦¦ª¨¨¬ªª­¬¬°¯¯±±±µµµ¹¹¹½½½ÀÀÀÄÂÂÄÄÄÉÉÉÍÍÍÑÑÑÕÕÕÙÙÙÞÞÞáááåååÿÿÿ    %  &%%  && %&#   ''&#%$%   #&&% %#  $    ##"%   ###              squeezelite-1.9.9-1395+git20220104.874e4f9/slimproto.c000066400000000000000000000652471416534420200213060ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additions (c) Paul Hermann, 2015-2021 under the same license terms * -Control of Raspberry pi GPIO for amplifier power * -Launch script on power status change from LMS */ #include "squeezelite.h" #include "slimproto.h" static log_level loglevel; #define SQUEEZENETWORK "mysqueezebox.com:3483" #define PORT 3483 #define MAXBUF 4096 #if SL_LITTLE_ENDIAN #define LOCAL_PLAYER_IP 0x0100007f // 127.0.0.1 #define LOCAL_PLAYER_PORT 0x9b0d // 3483 #else #define LOCAL_PLAYER_IP 0x7f000001 // 127.0.0.1 #define LOCAL_PLAYER_PORT 0x0d9b // 3483 #endif static sockfd sock = -1; static in_addr_t slimproto_ip = 0; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct codec *codecs[]; #if IR extern struct irstate ir; #endif event_event wake_e; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #define LOCK_D mutex_lock(decode.mutex) #define UNLOCK_D mutex_unlock(decode.mutex) #if IR #define LOCK_I mutex_lock(ir.mutex) #define UNLOCK_I mutex_unlock(ir.mutex) #endif static struct { u32_t updated; u32_t stream_start; u32_t stream_full; u32_t stream_size; u64_t stream_bytes; u32_t output_full; u32_t output_size; u32_t frames_played; u32_t device_frames; u32_t current_sample_rate; u32_t last; stream_state stream_state; } status; int autostart; bool sentSTMu, sentSTMo, sentSTMl; u32_t new_server; char *new_server_cap; #define PLAYER_NAME_LEN 64 char player_name[PLAYER_NAME_LEN + 1] = ""; const char *name_file = NULL; void send_packet(u8_t *packet, size_t len) { u8_t *ptr = packet; unsigned try = 0; ssize_t n; int error; while (len) { n = send(sock, ptr, len, MSG_NOSIGNAL); if (n <= 0) { error = last_error(); #if WIN if (n < 0 && (error == ERROR_WOULDBLOCK || error == WSAENOTCONN) && try < 10) { #else if (n < 0 && error == ERROR_WOULDBLOCK && try < 10) { #endif LOG_DEBUG("retrying (%d) writing to socket", ++try); usleep(1000); continue; } LOG_WARN("failed writing to socket: %s", strerror(last_error())); return; } ptr += n; len -= n; } } static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap, u8_t mac[6]) { #define BASE_CAP "Model=squeezelite,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Balance=1,Firmware=" VERSION #define SSL_CAP "CanHTTPS=1" const char *base_cap; struct HELO_packet pkt; #if USE_SSL #if !LINKALL && !NO_SSLSYM if (ssl_loaded) base_cap = SSL_CAP "," BASE_CAP; else base_cap = BASE_CAP; #endif base_cap = SSL_CAP "," BASE_CAP; #else base_cap = BASE_CAP; #endif memset(&pkt, 0, sizeof(pkt)); memcpy(&pkt.opcode, "HELO", 4); pkt.length = htonl(sizeof(struct HELO_packet) - 8 + strlen(base_cap) + strlen(fixed_cap) + strlen(var_cap)); pkt.deviceid = 12; // squeezeplay pkt.revision = 0; packn(&pkt.wlan_channellist, reconnect ? 0x4000 : 0x0000); packN(&pkt.bytes_received_H, (u64_t)status.stream_bytes >> 32); packN(&pkt.bytes_received_L, (u64_t)status.stream_bytes & 0xffffffff); memcpy(pkt.mac, mac, 6); LOG_INFO("mac: %02x:%02x:%02x:%02x:%02x:%02x", pkt.mac[0], pkt.mac[1], pkt.mac[2], pkt.mac[3], pkt.mac[4], pkt.mac[5]); LOG_INFO("cap: %s%s%s", base_cap, fixed_cap, var_cap); send_packet((u8_t *)&pkt, sizeof(pkt)); send_packet((u8_t *)base_cap, strlen(base_cap)); send_packet((u8_t *)fixed_cap, strlen(fixed_cap)); send_packet((u8_t *)var_cap, strlen(var_cap)); } static void sendSTAT(const char *event, u32_t server_timestamp) { struct STAT_packet pkt; u32_t now = gettime_ms(); u32_t ms_played; if (status.current_sample_rate && status.frames_played && status.frames_played > status.device_frames) { ms_played = (u32_t)(((u64_t)(status.frames_played - status.device_frames) * (u64_t)1000) / (u64_t)status.current_sample_rate); if (now > status.updated) ms_played += (now - status.updated); LOG_SDEBUG("ms_played: %u (frames_played: %u device_frames: %u)", ms_played, status.frames_played, status.device_frames); } else if (status.frames_played && now > status.stream_start) { ms_played = now - status.stream_start; LOG_SDEBUG("ms_played: %u using elapsed time (frames_played: %u device_frames: %u)", ms_played, status.frames_played, status.device_frames); } else { LOG_SDEBUG("ms_played: 0"); ms_played = 0; } memset(&pkt, 0, sizeof(struct STAT_packet)); memcpy(&pkt.opcode, "STAT", 4); pkt.length = htonl(sizeof(struct STAT_packet) - 8); memcpy(&pkt.event, event, 4); // num_crlf // mas_initialized; mas_mode; packN(&pkt.stream_buffer_fullness, status.stream_full); packN(&pkt.stream_buffer_size, status.stream_size); packN(&pkt.bytes_received_H, (u64_t)status.stream_bytes >> 32); packN(&pkt.bytes_received_L, (u64_t)status.stream_bytes & 0xffffffff); pkt.signal_strength = 0xffff; packN(&pkt.jiffies, now); packN(&pkt.output_buffer_size, status.output_size); packN(&pkt.output_buffer_fullness, status.output_full); packN(&pkt.elapsed_seconds, ms_played / 1000); // voltage; packN(&pkt.elapsed_milliseconds, ms_played); pkt.server_timestamp = server_timestamp; // keep this is server format - don't unpack/pack // error_code; LOG_DEBUG("STAT: %s", event); if (loglevel == lSDEBUG) { LOG_SDEBUG("received bytesL: %u streambuf: %u outputbuf: %u calc elapsed: %u real elapsed: %u (diff: %d) device: %u delay: %d", (u32_t)status.stream_bytes, status.stream_full, status.output_full, ms_played, now - status.stream_start, ms_played - now + status.stream_start, status.device_frames * 1000 / status.current_sample_rate, now - status.updated); } send_packet((u8_t *)&pkt, sizeof(pkt)); } static void sendDSCO(disconnect_code disconnect) { struct DSCO_packet pkt; memset(&pkt, 0, sizeof(pkt)); memcpy(&pkt.opcode, "DSCO", 4); pkt.length = htonl(sizeof(pkt) - 8); pkt.reason = disconnect & 0xFF; LOG_DEBUG("DSCO: %d", disconnect); send_packet((u8_t *)&pkt, sizeof(pkt)); } static void sendRESP(const char *header, size_t len) { struct RESP_header pkt_header; memset(&pkt_header, 0, sizeof(pkt_header)); memcpy(&pkt_header.opcode, "RESP", 4); pkt_header.length = htonl(sizeof(pkt_header) + len - 8); LOG_DEBUG("RESP"); send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); send_packet((u8_t *)header, len); } static void sendMETA(const char *meta, size_t len) { struct META_header pkt_header; memset(&pkt_header, 0, sizeof(pkt_header)); memcpy(&pkt_header.opcode, "META", 4); pkt_header.length = htonl(sizeof(pkt_header) + len - 8); LOG_DEBUG("META"); send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); send_packet((u8_t *)meta, len); } static void sendSETDName(const char *name) { struct SETD_header pkt_header; memset(&pkt_header, 0, sizeof(pkt_header)); memcpy(&pkt_header.opcode, "SETD", 4); pkt_header.id = 0; // id 0 is playername S:P:Squeezebox2 pkt_header.length = htonl(sizeof(pkt_header) + strlen(name) + 1 - 8); LOG_DEBUG("set playername: %s", name); send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); send_packet((u8_t *)name, strlen(name) + 1); } #if IR void sendIR(u32_t code, u32_t ts) { struct IR_packet pkt; memset(&pkt, 0, sizeof(pkt)); memcpy(&pkt.opcode, "IR ", 4); pkt.length = htonl(sizeof(pkt) - 8); packN(&pkt.jiffies, ts); pkt.ir_code = htonl(code); LOG_DEBUG("IR: ir code: 0x%x ts: %u", code, ts); send_packet((u8_t *)&pkt, sizeof(pkt)); } #endif static void process_strm(u8_t *pkt, int len) { struct strm_packet *strm = (struct strm_packet *)pkt; LOG_DEBUG("strm command %c", strm->command); switch(strm->command) { case 't': sendSTAT("STMt", strm->replay_gain); // STMt replay_gain is no longer used to track latency, but support it break; case 'q': decode_flush(); output_flush(); status.frames_played = 0; stream_disconnect(); sendSTAT("STMf", 0); buf_flush(streambuf); break; case 'f': decode_flush(); output_flush(); status.frames_played = 0; if (stream_disconnect()) { sendSTAT("STMf", 0); } buf_flush(streambuf); break; case 'p': { unsigned interval = unpackN(&strm->replay_gain); LOCK_O; output.pause_frames = interval * status.current_sample_rate / 1000; if (interval) { output.state = OUTPUT_PAUSE_FRAMES; } else if (output.state != OUTPUT_OFF) { output.state = OUTPUT_STOPPED; output.stop_time = gettime_ms(); } UNLOCK_O; if (!interval) sendSTAT("STMp", 0); LOG_DEBUG("pause interval: %u", interval); } break; case 'a': { unsigned interval = unpackN(&strm->replay_gain); LOCK_O; output.skip_frames = interval * status.current_sample_rate / 1000; output.state = OUTPUT_SKIP_FRAMES; UNLOCK_O; LOG_DEBUG("skip ahead interval: %u", interval); } break; case 'u': { unsigned jiffies = unpackN(&strm->replay_gain); LOCK_O; output.state = jiffies ? OUTPUT_START_AT : OUTPUT_RUNNING; output.start_at = jiffies; UNLOCK_O; LOG_DEBUG("unpause at: %u now: %u", jiffies, gettime_ms()); sendSTAT("STMr", 0); } break; case 's': { unsigned header_len = len - sizeof(struct strm_packet); char *header = (char *)(pkt + sizeof(struct strm_packet)); in_addr_t ip = (in_addr_t)strm->server_ip; // keep in network byte order u16_t port = strm->server_port; // keep in network byte order if (ip == 0) ip = slimproto_ip; LOG_DEBUG("strm s autostart: %c transition period: %u transition type: %u codec: %c", strm->autostart, strm->transition_period, strm->transition_type - '0', strm->format); autostart = strm->autostart - '0'; sendSTAT("STMf", 0); if (header_len > MAX_HEADER -1) { LOG_WARN("header too long: %u", header_len); break; } if (strm->format != '?') { codec_open(strm->format, strm->pcm_sample_size, strm->pcm_sample_rate, strm->pcm_channels, strm->pcm_endianness); } else if (autostart >= 2) { // extension to slimproto to allow server to detect codec from response header and send back in codc message LOG_DEBUG("streaming unknown codec"); } else { LOG_WARN("unknown codec requires autostart >= 2"); break; } if (ip == LOCAL_PLAYER_IP && port == LOCAL_PLAYER_PORT) { // extension to slimproto for LocalPlayer - header is filename not http header, don't expect cont stream_file(header, header_len, strm->threshold * 1024); autostart -= 2; } else { stream_sock(ip, port, strm->flags & 0x20, header, header_len, strm->threshold * 1024, autostart >= 2); } sendSTAT("STMc", 0); sentSTMu = sentSTMo = sentSTMl = false; LOCK_O; output.threshold = strm->output_threshold; output.next_replay_gain = unpackN(&strm->replay_gain); output.fade_mode = strm->transition_type - '0'; output.fade_secs = strm->transition_period; output.invert = (strm->flags & 0x03) == 0x03; output.channels = (strm->flags & 0x0c) >> 2; LOG_DEBUG("set fade mode: %u, channels: %u, invert: %u", output.fade_mode, output.channels, output.invert); UNLOCK_O; } break; default: LOG_WARN("unhandled strm %c", strm->command); break; } } static void process_cont(u8_t *pkt, int len) { struct cont_packet *cont = (struct cont_packet *)pkt; cont->metaint = unpackN(&cont->metaint); LOG_DEBUG("cont metaint: %u loop: %u", cont->metaint, cont->loop); if (autostart > 1) { autostart -= 2; LOCK_S; if (stream.state == STREAMING_WAIT) { stream.state = STREAMING_BUFFERING; stream.meta_interval = stream.meta_next = cont->metaint; } UNLOCK_S; wake_controller(); } } static void process_codc(u8_t *pkt, int len) { struct codc_packet *codc = (struct codc_packet *)pkt; LOG_DEBUG("codc: %c", codc->format); codec_open(codc->format, codc->pcm_sample_size, codc->pcm_sample_rate, codc->pcm_channels, codc->pcm_endianness); } static void process_aude(u8_t *pkt, int len) { struct aude_packet *aude = (struct aude_packet *)pkt; LOG_DEBUG("enable spdif: %d dac: %d", aude->enable_spdif, aude->enable_dac); LOCK_O; if (!aude->enable_spdif && output.state != OUTPUT_OFF) { output.state = OUTPUT_OFF; } if (aude->enable_spdif && output.state == OUTPUT_OFF && !output.idle_to) { output.state = OUTPUT_STOPPED; output.stop_time = gettime_ms(); } UNLOCK_O; } static void process_audg(u8_t *pkt, int len) { struct audg_packet *audg = (struct audg_packet *)pkt; audg->gainL = unpackN(&audg->gainL); audg->gainR = unpackN(&audg->gainR); LOG_DEBUG("audg gainL: %u gainR: %u adjust: %u", audg->gainL, audg->gainR, audg->adjust); set_volume(audg->adjust ? audg->gainL : FIXED_ONE, audg->adjust ? audg->gainR : FIXED_ONE); } static void process_setd(u8_t *pkt, int len) { struct setd_packet *setd = (struct setd_packet *)pkt; // handle player name query and change if (setd->id == 0) { if (len == 5) { if (strlen(player_name)) { sendSETDName(player_name); } } else if (len > 5) { strncpy(player_name, setd->data, PLAYER_NAME_LEN); player_name[PLAYER_NAME_LEN] = '\0'; LOG_INFO("set name: %s", setd->data); // confirm change to server sendSETDName(setd->data); // write name to name_file if -N option set if (name_file) { FILE *fp = fopen(name_file, "w"); if (fp) { LOG_INFO("storing name in %s", name_file); fputs(player_name, fp); fclose(fp); } else { LOG_WARN("unable to store new name in %s", name_file); } } } } } #define SYNC_CAP ",SyncgroupID=" #define SYNC_CAP_LEN 13 static void process_serv(u8_t *pkt, int len) { struct serv_packet *serv = (struct serv_packet *)pkt; unsigned slimproto_port = 0; char squeezeserver[] = SQUEEZENETWORK; if(pkt[4] == 0 && pkt[5] == 0 && pkt[6] == 0 && pkt[7] == 1) { server_addr(squeezeserver, &new_server, &slimproto_port); } else { new_server = serv->server_ip; } LOG_INFO("switch server"); if (len - sizeof(struct serv_packet) == 10) { if (!new_server_cap) { new_server_cap = malloc(SYNC_CAP_LEN + 10 + 1); } new_server_cap[0] = '\0'; strcat(new_server_cap, SYNC_CAP); strncat(new_server_cap, (const char *)(pkt + sizeof(struct serv_packet)), 10); } else { if (new_server_cap) { free(new_server_cap); new_server_cap = NULL; } } } struct handler { char opcode[5]; void (*handler)(u8_t *, int); }; static struct handler handlers[] = { { "strm", process_strm }, { "cont", process_cont }, { "codc", process_codc }, { "aude", process_aude }, { "audg", process_audg }, { "setd", process_setd }, { "serv", process_serv }, { "", NULL }, }; static void process(u8_t *pack, int len) { struct handler *h = handlers; while (h->handler && strncmp((char *)pack, h->opcode, 4)) { h++; } if (h->handler) { LOG_DEBUG("%s", h->opcode); h->handler(pack, len); } else { pack[4] = '\0'; LOG_WARN("unhandled %s", (char *)pack); } } static bool running; static void slimproto_run() { static u8_t buffer[MAXBUF]; int expect = 0; int got = 0; u32_t now; static u32_t last = 0; event_handle ehandles[2]; int timeouts = 0; set_readwake_handles(ehandles, sock, wake_e); while (running && !new_server) { bool wake = false; event_type ev; if ((ev = wait_readwake(ehandles, 1000)) != EVENT_TIMEOUT) { if (ev == EVENT_READ) { if (expect > 0) { int n = recv(sock, buffer + got, expect, 0); if (n <= 0) { if (n < 0 && last_error() == ERROR_WOULDBLOCK) { continue; } LOG_INFO("error reading from socket: %s", n ? strerror(last_error()) : "closed"); return; } expect -= n; got += n; if (expect == 0) { process(buffer, got); got = 0; } } else if (expect == 0) { int n = recv(sock, buffer + got, 2 - got, 0); if (n <= 0) { if (n < 0 && last_error() == ERROR_WOULDBLOCK) { continue; } LOG_INFO("error reading from socket: %s", n ? strerror(last_error()) : "closed"); return; } got += n; if (got == 2) { expect = buffer[0] << 8 | buffer[1]; // length pack 'n' got = 0; if (expect > MAXBUF) { LOG_ERROR("FATAL: slimproto packet too big: %d > %d", expect, MAXBUF); return; } } } else { LOG_ERROR("FATAL: negative expect"); return; } } if (ev == EVENT_WAKE) { wake = true; } timeouts = 0; } else if (++timeouts > 35) { // expect message from server every 5 seconds, but 30 seconds on mysb.com so timeout after 35 seconds LOG_INFO("No messages from server - connection dead"); return; } // update playback state when woken or every 100ms now = gettime_ms(); if (wake || now - last > 100 || last > now) { bool _sendSTMs = false; bool _sendDSCO = false; bool _sendRESP = false; bool _sendMETA = false; bool _sendSTMd = false; bool _sendSTMt = false; bool _sendSTMl = false; bool _sendSTMu = false; bool _sendSTMo = false; bool _sendSTMn = false; bool _stream_disconnect = false; bool _start_output = false; decode_state _decode_state; disconnect_code disconnect_code; static char header[MAX_HEADER]; size_t header_len = 0; #if IR bool _sendIR = false; u32_t ir_code, ir_ts; #endif last = now; LOCK_S; status.stream_full = _buf_used(streambuf); status.stream_size = streambuf->size; status.stream_bytes = stream.bytes; status.stream_state = stream.state; if (stream.state == DISCONNECT) { disconnect_code = stream.disconnect; stream.state = STOPPED; _sendDSCO = true; } if (!stream.sent_headers && (stream.state == STREAMING_HTTP || stream.state == STREAMING_WAIT || stream.state == STREAMING_BUFFERING)) { header_len = stream.header_len; memcpy(header, stream.header, header_len); _sendRESP = true; stream.sent_headers = true; } if (stream.meta_send) { header_len = stream.header_len; memcpy(header, stream.header, header_len); _sendMETA = true; stream.meta_send = false; } UNLOCK_S; LOCK_D; if ((status.stream_state == STREAMING_HTTP || status.stream_state == STREAMING_FILE || (status.stream_state == DISCONNECT && stream.disconnect == DISCONNECT_OK)) && !sentSTMl && decode.state == DECODE_READY) { if (autostart == 0) { decode.state = DECODE_RUNNING; _sendSTMl = true; sentSTMl = true; } else if (autostart == 1) { decode.state = DECODE_RUNNING; _start_output = true; } // autostart 2 and 3 require cont to be received first } if (decode.state == DECODE_COMPLETE || decode.state == DECODE_ERROR) { if (decode.state == DECODE_COMPLETE) _sendSTMd = true; if (decode.state == DECODE_ERROR) _sendSTMn = true; decode.state = DECODE_STOPPED; if (status.stream_state == STREAMING_HTTP || status.stream_state == STREAMING_FILE) { _stream_disconnect = true; } } _decode_state = decode.state; UNLOCK_D; LOCK_O; status.output_full = _buf_used(outputbuf); status.output_size = outputbuf->size; status.frames_played = output.frames_played_dmp; status.current_sample_rate = output.current_sample_rate; status.updated = output.updated; status.device_frames = output.device_frames; if (output.track_started) { _sendSTMs = true; output.track_started = false; status.stream_start = output.track_start_time; } #if PORTAUDIO if (output.pa_reopen) { _pa_open(); output.pa_reopen = false; } #endif if (_start_output && (output.state == OUTPUT_STOPPED || output.state == OUTPUT_OFF)) { output.state = OUTPUT_BUFFER; } if (output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT && _decode_state == DECODE_STOPPED) { _sendSTMu = true; sentSTMu = true; LOG_DEBUG("output underrun"); output.state = OUTPUT_STOPPED; output.stop_time = now; } if (output.state == OUTPUT_RUNNING && !sentSTMo && status.output_full == 0 && status.stream_state == STREAMING_HTTP) { _sendSTMo = true; sentSTMo = true; } if (output.state == OUTPUT_STOPPED && output.idle_to && (now - output.stop_time > output.idle_to)) { output.state = OUTPUT_OFF; LOG_DEBUG("output timeout"); } if (output.state == OUTPUT_RUNNING && now - status.last > 1000) { _sendSTMt = true; status.last = now; } UNLOCK_O; #if IR LOCK_I; if (ir.code) { _sendIR = true; ir_code = ir.code; ir_ts = ir.ts; ir.code = 0; } UNLOCK_I; #endif if (_stream_disconnect) stream_disconnect(); // send packets once locks released as packet sending can block if (_sendDSCO) sendDSCO(disconnect_code); if (_sendSTMs) sendSTAT("STMs", 0); if (_sendSTMd) sendSTAT("STMd", 0); if (_sendSTMt) sendSTAT("STMt", 0); if (_sendSTMl) sendSTAT("STMl", 0); if (_sendSTMu) sendSTAT("STMu", 0); if (_sendSTMo) sendSTAT("STMo", 0); if (_sendSTMn) sendSTAT("STMn", 0); if (_sendRESP) sendRESP(header, header_len); if (_sendMETA) sendMETA(header, header_len); #if IR if (_sendIR) sendIR(ir_code, ir_ts); #endif } } } // called from other threads to wake state machine above void wake_controller(void) { wake_signal(wake_e); } in_addr_t discover_server(char *default_server) { struct sockaddr_in d; struct sockaddr_in s; char *buf; struct pollfd pollinfo; unsigned port; int disc_sock = socket(AF_INET, SOCK_DGRAM, 0); socklen_t enable = 1; setsockopt(disc_sock, SOL_SOCKET, SO_BROADCAST, (const void *)&enable, sizeof(enable)); buf = "e"; memset(&d, 0, sizeof(d)); d.sin_family = AF_INET; d.sin_port = htons(PORT); d.sin_addr.s_addr = htonl(INADDR_BROADCAST); pollinfo.fd = disc_sock; pollinfo.events = POLLIN; do { LOG_INFO("sending discovery"); memset(&s, 0, sizeof(s)); if (sendto(disc_sock, buf, 1, 0, (struct sockaddr *)&d, sizeof(d)) < 0) { LOG_INFO("error sending disovery"); } if (poll(&pollinfo, 1, 5000) == 1) { char readbuf[10]; socklen_t slen = sizeof(s); recvfrom(disc_sock, readbuf, 10, 0, (struct sockaddr *)&s, &slen); LOG_INFO("got response from: %s:%d", inet_ntoa(s.sin_addr), ntohs(s.sin_port)); } if (default_server) { server_addr(default_server, &s.sin_addr.s_addr, &port); } } while (s.sin_addr.s_addr == 0 && running); closesocket(disc_sock); return s.sin_addr.s_addr; } #define FIXED_CAP_LEN 256 #define VAR_CAP_LEN 128 void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile, const char *modelname, int maxSampleRate) { struct sockaddr_in serv_addr; static char fixed_cap[FIXED_CAP_LEN], var_cap[VAR_CAP_LEN] = ""; bool reconnect = false; unsigned failed_connect = 0; unsigned slimproto_port = 0; in_addr_t previous_server = 0; int i; memset(&status, 0, sizeof(status)); wake_create(wake_e); loglevel = level; running = true; if (server) { server_addr(server, &slimproto_ip, &slimproto_port); } if (!slimproto_ip) { slimproto_ip = discover_server(server); } if (!slimproto_port) { slimproto_port = PORT; } if (name) { strncpy(player_name, name, PLAYER_NAME_LEN); player_name[PLAYER_NAME_LEN] = '\0'; } if (namefile) { FILE *fp; name_file = namefile; fp = fopen(namefile, "r"); if (fp) { if (!fgets(player_name, PLAYER_NAME_LEN, fp)) { player_name[PLAYER_NAME_LEN] = '\0'; } else { // strip any \n from fgets response int len = strlen(player_name); if (len > 0 && player_name[len - 1] == '\n') { player_name[len - 1] = '\0'; } LOG_INFO("retrieved name %s from %s", player_name, name_file); } fclose(fp); } } if (!running) return; LOCK_O; snprintf(fixed_cap, FIXED_CAP_LEN, ",ModelName=%s,MaxSampleRate=%u", modelname ? modelname : MODEL_NAME_STRING, #if RESAMPLE ((maxSampleRate > 0) ? maxSampleRate : output.supported_rates[0])); #else ((maxSampleRate > 0 && maxSampleRate < output.supported_rates[0]) ? maxSampleRate : output.supported_rates[0])); #endif for (i = 0; i < MAX_CODECS; i++) { if (codecs[i] && codecs[i]->id && strlen(fixed_cap) < FIXED_CAP_LEN - 10) { strcat(fixed_cap, ","); strcat(fixed_cap, codecs[i]->types); } } UNLOCK_O; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = slimproto_ip; serv_addr.sin_port = htons(slimproto_port); LOG_INFO("connecting to %s:%d", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port)); new_server = 0; while (running) { if (new_server) { previous_server = slimproto_ip; slimproto_ip = serv_addr.sin_addr.s_addr = new_server; LOG_INFO("switching server to %s:%d", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port)); new_server = 0; reconnect = false; } sock = socket(AF_INET, SOCK_STREAM, 0); set_nonblock(sock); set_nosigpipe(sock); if (connect_timeout(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr), 5) != 0) { if (previous_server) { slimproto_ip = serv_addr.sin_addr.s_addr = previous_server; LOG_INFO("new server not reachable, reverting to previous server %s:%d", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port)); } else { LOG_INFO("unable to connect to server %u", failed_connect); sleep(5); } // rediscover server if it was not set at startup if (!server && ++failed_connect > 5) { slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(NULL); } } else { struct sockaddr_in our_addr; socklen_t len; LOG_INFO("connected"); var_cap[0] = '\0'; failed_connect = 0; // check if this is a local player now we are connected & signal to server via 'loc' format // this requires LocalPlayer server plugin to enable direct file access len = sizeof(our_addr); getsockname(sock, (struct sockaddr *) &our_addr, &len); if (our_addr.sin_addr.s_addr == serv_addr.sin_addr.s_addr) { LOG_INFO("local player"); strcat(var_cap, ",loc"); } // add on any capablity to be sent to the new server if (new_server_cap) { strcat(var_cap, new_server_cap); free(new_server_cap); new_server_cap = NULL; } sendHELO(reconnect, fixed_cap, var_cap, mac); slimproto_run(); if (!reconnect) { reconnect = true; } usleep(100000); } previous_server = 0; closesocket(sock); } } void slimproto_stop(void) { LOG_INFO("slimproto stop"); running = false; } squeezelite-1.9.9-1395+git20220104.874e4f9/slimproto.h000066400000000000000000000072301416534420200212770ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // packet formats for slimproto #ifndef SUN #pragma pack(push, 1) #else #pragma pack(1) #endif // from S:N:Slimproto _hello_handler struct HELO_packet { char opcode[4]; u32_t length; u8_t deviceid; u8_t revision; u8_t mac[6]; u8_t uuid[16]; u16_t wlan_channellist; u32_t bytes_received_H, bytes_received_L; char lang[2]; // u8_t capabilities[]; }; // S:N:Slimproto _stat_handler struct STAT_packet { char opcode[4]; u32_t length; u32_t event; u8_t num_crlf; u8_t mas_initialized; u8_t mas_mode; u32_t stream_buffer_size; u32_t stream_buffer_fullness; u32_t bytes_received_H; u32_t bytes_received_L; u16_t signal_strength; u32_t jiffies; u32_t output_buffer_size; u32_t output_buffer_fullness; u32_t elapsed_seconds; u16_t voltage; u32_t elapsed_milliseconds; u32_t server_timestamp; u16_t error_code; }; // S:N:Slimproto _disco_handler struct DSCO_packet { char opcode[4]; u32_t length; u8_t reason; }; // S:N:Slimproto _http_response_handler struct RESP_header { char opcode[4]; u32_t length; // char header[] - added in sendRESP }; // S:N:Slimproto _http_metadata_handler struct META_header { char opcode[4]; u32_t length; // char metadata[] }; // S:N:Slimproto _http_setting_handler struct SETD_header { char opcode[4]; u32_t length; u8_t id; // data }; #if IR struct IR_packet { char opcode[4]; u32_t length; u32_t jiffies; u8_t format; // ignored by server u8_t bits; // ignored by server u32_t ir_code; }; #endif // from S:P:Squeezebox stream_s struct strm_packet { char opcode[4]; char command; u8_t autostart; u8_t format; u8_t pcm_sample_size; u8_t pcm_sample_rate; u8_t pcm_channels; u8_t pcm_endianness; u8_t threshold; u8_t spdif_enable; u8_t transition_period; u8_t transition_type; u8_t flags; u8_t output_threshold; u8_t slaves; u32_t replay_gain; u16_t server_port; u32_t server_ip; //char request_string[]; }; // S:P:Squeezebox2 struct aude_packet { char opcode[4]; u8_t enable_spdif; u8_t enable_dac; }; // S:P:Squeezebox2 struct audg_packet { char opcode[4]; u32_t old_gainL; // unused u32_t old_gainR; // unused u8_t adjust; u8_t preamp; // unused u32_t gainL; u32_t gainR; // squence ids - unused }; // S:P:Squeezebox2 struct cont_packet { char opcode[4]; u32_t metaint; u8_t loop; // guids we don't use }; // S:C:Commands struct serv_packet { char opcode[4]; u32_t server_ip; // possible sync group }; // S:P:Squeezebox2 struct setd_packet { char opcode[4]; u8_t id; char data[]; }; // codec open - this is an extension to slimproto to allow the server to read the header and then return decode params struct codc_packet { char opcode[4]; u8_t format; u8_t pcm_sample_size; u8_t pcm_sample_rate; u8_t pcm_channels; u8_t pcm_endianness; }; #ifndef SUN #pragma pack(pop) #else #pragma pack() #endif squeezelite-1.9.9-1395+git20220104.874e4f9/squeezelite-dll.sln000077500000000000000000000016041416534420200227260ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 10.00 # Visual C++ Express 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "squeezelite-dll", "squeezelite-dll.vcproj", "{7CB5A216-CEFA-4932-93CD-E9BFA33DD349}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|Win32.ActiveCfg = Debug|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|Win32.Build.0 = Debug|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|Win32.ActiveCfg = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal squeezelite-1.9.9-1395+git20220104.874e4f9/squeezelite-dll.vcproj000077500000000000000000000162121416534420200234360ustar00rootroot00000000000000 squeezelite-1.9.9-1395+git20220104.874e4f9/squeezelite-x64.sln000066400000000000000000000026671416534420200226030ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.28307.1062 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "squeezelite-x64", "squeezelite-x64.vcxproj", "{7CB5A216-CEFA-4932-93CD-E9BFA33DD349}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|Win32.ActiveCfg = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|Win32.Build.0 = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|x64.ActiveCfg = Debug|x64 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|x64.Build.0 = Debug|x64 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|Win32.ActiveCfg = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|Win32.Build.0 = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|x64.ActiveCfg = Release|x64 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB7D547A-95FA-4A5F-82FC-E013A35756B4} EndGlobalSection EndGlobal squeezelite-1.9.9-1395+git20220104.874e4f9/squeezelite-x64.vcxproj000066400000000000000000000255711416534420200235010ustar00rootroot00000000000000 Debug Win32 Debug x64 Release Win32 Release x64 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349} squeezelite Win32Proj Application v141 Application v141 Application v141 Application v141 <_ProjectFileVersion>15.0.28307.799 Debug\ Debug\ true true Release\ Release\ false false Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL Level3 EditAndContinue true Console MachineX86 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebugDLL Level3 ProgramDatabase true Console include\alac;include\opus;include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;RESAMPLE;ALAC;OPUS;DSD;USE_SSL;FLAC__NO_DLL;LINKALL;%(PreprocessorDefinitions) MultiThreaded Level3 /NODEFAULTLIB:LIBCMT %(AdditionalOptions) ws2_32.lib;lib\portaudio.lib;lib\libogg.lib;lib\libvorbisfile.lib;lib\libvorbis.lib;lib\libmad.lib;lib\libFLAC.lib;lib\libfaad.lib;lib\libmpg123.lib;lib\libsoxr.lib;lib\libalac.lib;lib\ssleay32.lib;lib\libeay32.lib;lib\opus.lib;lib\opusfile.lib;%(AdditionalDependencies) $(OutDir)$(ProjectName).exe false Console true true MachineX86 include\alac;include\opus;include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;RESAMPLE;DSD;ALAC;OPUS;USE_SSL;FLAC__NO_DLL;LINKALL;%(PreprocessorDefinitions) MultiThreaded Level3 /NODEFAULTLIB:LIBCMT %(AdditionalOptions) ws2_32.lib;crypt32.lib;lib\portaudio.lib;lib\libogg.lib;lib\libvorbisfile.lib;lib\libvorbis.lib;lib\libmad.lib;lib\libFLAC.lib;lib\libfaad.lib;lib\libmpg123.lib;lib\libsoxr.lib;lib\libalac.lib;lib\libssl.lib;lib\libcrypto.lib;lib\opus.lib;lib\opusfile.lib;%(AdditionalDependencies) $(OutDir)$(ProjectName).exe false Console true true squeezelite-1.9.9-1395+git20220104.874e4f9/squeezelite-x86.sln000066400000000000000000000016031416534420200225740ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "squeezelite-x86", "squeezelite-x86.vcproj", "{7CB5A216-CEFA-4932-93CD-E9BFA33DD349}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|Win32.ActiveCfg = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|Win32.Build.0 = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|Win32.ActiveCfg = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal squeezelite-1.9.9-1395+git20220104.874e4f9/squeezelite-x86.vcproj000077500000000000000000000170051416534420200233110ustar00rootroot00000000000000 squeezelite-1.9.9-1395+git20220104.874e4f9/squeezelite.h000066400000000000000000000533201416534420200216070ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additions (c) Paul Hermann, 2015-2021 under the same license terms * -Control of Raspberry pi GPIO for amplifier power * -Launch script on power status change from LMS */ // make may define: PORTAUDIO, SELFPIPE, RESAMPLE, RESAMPLE_MP, VISEXPORT, GPIO, IR, DSD, LINKALL to influence build #define MAJOR_VERSION "1.9" #define MINOR_VERSION "9" #define MICRO_VERSION "1395" #if defined(CUSTOM_VERSION) #define VERSION "v" MAJOR_VERSION "." MINOR_VERSION "-" MICRO_VERSION STR(CUSTOM_VERSION) #else #define VERSION "v" MAJOR_VERSION "." MINOR_VERSION "-" MICRO_VERSION #endif #if !defined(MODEL_NAME) #define MODEL_NAME SqueezeLite #endif #define QUOTE(name) #name #define STR(macro) QUOTE(macro) #define MODEL_NAME_STRING STR(MODEL_NAME) // build detection #if defined(linux) #define LINUX 1 #define OSX 0 #define WIN 0 #define FREEBSD 0 #elif defined (__APPLE__) #define LINUX 0 #define OSX 1 #define WIN 0 #define PORTAUDIO 1 #define FREEBSD 0 #elif defined (_MSC_VER) #define LINUX 0 #define OSX 0 #define WIN 1 #define PORTAUDIO 1 #define FREEBSD 0 #elif defined(__FreeBSD__) #define LINUX 0 #define OSX 0 #define WIN 0 #define PORTAUDIO 1 #define FREEBSD 1 #elif defined (__sun) #define SUN 1 #define LINUX 1 #define PORTAUDIO 1 #define PA18API 1 #define OSX 0 #define WIN 0 #else #error unknown target #endif #if LINUX && !defined(PORTAUDIO) && !defined(PULSEAUDIO) #define ALSA 1 #else #define ALSA 0 #endif #if SUN #define EVENTFD 0 #define WINEVENT 0 #define SELFPIPE 1 #elif LINUX && !defined(SELFPIPE) #define EVENTFD 1 #define SELFPIPE 0 #define WINEVENT 0 #endif #if (LINUX && !EVENTFD) || OSX || FREEBSD #define EVENTFD 0 #define SELFPIPE 1 #define WINEVENT 0 #endif #if WIN #define EVENTFD 0 #define SELFPIPE 0 #define WINEVENT 1 #endif #if defined(RESAMPLE) || defined(RESAMPLE_MP) #undef RESAMPLE #define RESAMPLE 1 // resampling #define PROCESS 1 // any sample processing (only resampling at present) #else #define RESAMPLE 0 #define PROCESS 0 #endif #if defined(RESAMPLE_MP) #undef RESAMPLE_MP #define RESAMPLE_MP 1 #else #define RESAMPLE_MP 0 #endif #if defined(ALAC) #undef ALAC #define ALAC 1 #else #define ALAC 0 #endif #if defined(FFMPEG) #undef FFMPEG #define FFMPEG 1 #else #define FFMPEG 0 #endif #if defined(OPUS) #undef OPUS #define OPUS 1 #else #define OPUS 0 #endif #if (LINUX || OSX) && defined(VISEXPORT) #undef VISEXPORT #define VISEXPORT 1 // visulizer export support uses linux shared memory #else #define VISEXPORT 0 #endif #if LINUX && defined(IR) #undef IR #define IR 1 #else #define IR 0 #endif #if defined(DSD) #undef DSD #define DSD 1 #define IF_DSD(x) { x } #else #undef DSD #define DSD 0 #define IF_DSD(x) #endif #if defined(LINKALL) #undef LINKALL #define LINKALL 1 // link all libraries at build time - requires all to be available at run time #else #define LINKALL 0 #endif #if defined (USE_SSL) #undef USE_SSL #define USE_SSL 1 #else #define USE_SSL 0 #endif #if defined (NO_SSLSYM) #undef NO_SSLSYM #define NO_SSLSYM 1 #else #define NO_SSLSYM 0 #endif #if !LINKALL // dynamically loaded libraries at run time #if LINUX #define LIBFLAC "libFLAC.so.8" #define LIBMAD "libmad.so.0" #define LIBMPG "libmpg123.so.0" #define LIBVORBIS "libvorbisfile.so.3" #define LIBOPUS "libopusfile.so.0" #define LIBTREMOR "libvorbisidec.so.1" #define LIBFAAD "libfaad.so.2" #define LIBAVUTIL "libavutil.so.%d" #define LIBAVCODEC "libavcodec.so.%d" #define LIBAVFORMAT "libavformat.so.%d" #define LIBSOXR "libsoxr.so.0" #define LIBLIRC "liblirc_client.so.0" #endif #if OSX #define LIBFLAC "libFLAC.8.dylib" #define LIBMAD "libmad.0.dylib" #define LIBMPG "libmpg123.0.dylib" #define LIBVORBIS "libvorbisfile.3.dylib" #define LIBTREMOR "libvorbisidec.1.dylib" #define LIBOPUS "libopusfile.0.dylib" #define LIBFAAD "libfaad.2.dylib" #define LIBAVUTIL "libavutil.%d.dylib" #define LIBAVCODEC "libavcodec.%d.dylib" #define LIBAVFORMAT "libavformat.%d.dylib" #define LIBSOXR "libsoxr.0.dylib" #endif #if WIN #define LIBFLAC "libFLAC.dll" #define LIBMAD "libmad-0.dll" #define LIBMPG "libmpg123-0.dll" #define LIBVORBIS "libvorbisfile.dll" #define LIBOPUS "libopusfile-0.dll" #define LIBTREMOR "libvorbisidec.dll" #define LIBFAAD "libfaad2.dll" #define LIBAVUTIL "avutil-%d.dll" #define LIBAVCODEC "avcodec-%d.dll" #define LIBAVFORMAT "avformat-%d.dll" #define LIBSOXR "libsoxr.dll" #endif #if FREEBSD #define LIBFLAC "libFLAC.so.8" #define LIBMAD "libmad.so.0" #define LIBMPG "libmpg123.so.0" #define LIBVORBIS "libvorbisfile.so.3" #define LIBTREMOR "libvorbisidec.so.1" #define LIBOPUS "libopusfile.so.1" #define LIBFAAD "libfaad.so.2" #define LIBAVUTIL "libavutil.so.%d" #define LIBAVCODEC "libavcodec.so.%d" #define LIBAVFORMAT "libavformat.so.%d" #define LIBSOXR "libsoxr.so.0" #endif #endif // !LINKALL // config options #define STREAMBUF_SIZE (2 * 1024 * 1024) #define OUTPUTBUF_SIZE (44100 * 8 * 10) #define OUTPUTBUF_SIZE_CROSSFADE (OUTPUTBUF_SIZE * 12 / 10) #define MAX_HEADER 4096 // do not reduce as icy-meta max is 4080 #if ALSA #define ALSA_BUFFER_TIME 40 #define ALSA_PERIOD_COUNT 4 #define OUTPUT_RT_PRIORITY 45 #endif #define SL_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) #if SUN || OSXPPC #undef SL_LITTLE_ENDIAN #endif #include #include #include #include #include #include #include #if LINUX || OSX || FREEBSD #include #include #include #include #include #include #include #include #include #include #if SUN #include #endif /* SUN */ #define STREAM_THREAD_STACK_SIZE 64 * 1024 #define DECODE_THREAD_STACK_SIZE 128 * 1024 #define OUTPUT_THREAD_STACK_SIZE 64 * 1024 #define IR_THREAD_STACK_SIZE 64 * 1024 #if !OSX #define thread_t pthread_t; #endif #define closesocket(s) close(s) #define last_error() errno #define ERROR_WOULDBLOCK EWOULDBLOCK #ifdef SUN typedef uint8_t u8_t; typedef uint16_t u16_t; typedef uint32_t u32_t; typedef uint64_t u64_t; #else typedef u_int8_t u8_t; typedef u_int16_t u16_t; typedef u_int32_t u32_t; typedef u_int64_t u64_t; #endif /* SUN */ typedef int16_t s16_t; typedef int32_t s32_t; typedef int64_t s64_t; #define mutex_type pthread_mutex_t #define mutex_create(m) pthread_mutex_init(&m, NULL) #define mutex_create_p(m) pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); pthread_mutex_init(&m, &attr); pthread_mutexattr_destroy(&attr) #define mutex_lock(m) pthread_mutex_lock(&m) #define mutex_unlock(m) pthread_mutex_unlock(&m) #define mutex_destroy(m) pthread_mutex_destroy(&m) #define thread_type pthread_t #endif #if WIN #include #include #include #define STREAM_THREAD_STACK_SIZE (1024 * 64) #define DECODE_THREAD_STACK_SIZE (1024 * 128) #define OUTPUT_THREAD_STACK_SIZE (1024 * 64) typedef unsigned __int8 u8_t; typedef unsigned __int16 u16_t; typedef unsigned __int32 u32_t; typedef unsigned __int64 u64_t; typedef __int16 s16_t; typedef __int32 s32_t; typedef __int64 s64_t; typedef BOOL bool; #define true TRUE #define false FALSE #define inline __inline #define mutex_type HANDLE #define mutex_create(m) m = CreateMutex(NULL, FALSE, NULL) #define mutex_create_p mutex_create #define mutex_lock(m) WaitForSingleObject(m, INFINITE) #define mutex_unlock(m) ReleaseMutex(m) #define mutex_destroy(m) CloseHandle(m) #define thread_type HANDLE #define usleep(x) Sleep(x/1000) #define sleep(x) Sleep(x*1000) #define last_error() WSAGetLastError() #define ERROR_WOULDBLOCK WSAEWOULDBLOCK #define open _open #define read _read #define snprintf _snprintf #define in_addr_t u32_t #define socklen_t int #define ssize_t int #define RTLD_NOW 0 #endif #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif typedef u32_t frames_t; typedef int sockfd; #if EVENTFD #include #define event_event int #define event_handle struct pollfd #define wake_create(e) e = eventfd(0, 0) #define wake_signal(e) eventfd_write(e, 1) #define wake_clear(e) eventfd_t val; eventfd_read(e, &val) #define wake_close(e) close(e) #endif #if SELFPIPE #define event_handle struct pollfd #define event_event struct wake #define wake_create(e) pipe(e.fds); set_nonblock(e.fds[0]); set_nonblock(e.fds[1]) #define wake_signal(e) write(e.fds[1], ".", 1) #define wake_clear(e) char c[10]; read(e, &c, 10) #define wake_close(e) close(e.fds[0]); close(e.fds[1]) struct wake { int fds[2]; }; #endif #if WINEVENT #define event_event HANDLE #define event_handle HANDLE #define wake_create(e) e = CreateEvent(NULL, FALSE, FALSE, NULL) #define wake_signal(e) SetEvent(e) #define wake_close(e) CloseHandle(e) #endif // printf/scanf formats for u64_t #if (LINUX && __WORDSIZE == 64) || (FREEBSD && __LP64__) #define FMT_u64 "%lu" #define FMT_x64 "%lx" #elif __GLIBC_HAVE_LONG_LONG || defined __GNUC__ || WIN || SUN #define FMT_u64 "%llu" #define FMT_x64 "%llx" #else #error can not support u64_t #endif #define MAX_SILENCE_FRAMES 2048 #define FIXED_ONE 0x10000 #define BYTES_PER_FRAME 8 #if BYTES_PER_FRAME == 8 #define ISAMPLE_T s32_t #else #define ISAMPLE_T s16_t #endif #define min(a,b) (((a) < (b)) ? (a) : (b)) // logging 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__) #define LOG_SDEBUG(fmt, ...) if (loglevel >= lSDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__) // utils.c (non logging) typedef enum { EVENT_TIMEOUT = 0, EVENT_READ, EVENT_WAKE } event_type; #if WIN && USE_SSL char* strcasestr(const char *haystack, const char *needle); #endif char *next_param(char *src, char c); u32_t gettime_ms(void); void get_mac(u8_t *mac); void set_nonblock(sockfd s); int connect_timeout(sockfd sock, const struct sockaddr *addr, socklen_t addrlen, int timeout); void server_addr(char *server, in_addr_t *ip_ptr, unsigned *port_ptr); void set_readwake_handles(event_handle handles[], sockfd s, event_event e); event_type wait_readwake(event_handle handles[], int timeout); void packN(u32_t *dest, u32_t val); void packn(u16_t *dest, u16_t val); u32_t unpackN(u32_t *src); u16_t unpackn(u16_t *src); #if OSX void set_nosigpipe(sockfd s); #else #define set_nosigpipe(s) #endif #if SUN void init_daemonize(void); int daemon(int,int); #endif #if WIN void winsock_init(void); void winsock_close(void); void *dlopen(const char *filename, int flag); void *dlsym(void *handle, const char *symbol); char *dlerror(void); int poll(struct pollfd *fds, unsigned long numfds, int timeout); #endif #if LINUX || FREEBSD void touch_memory(u8_t *buf, size_t size); #endif // buffer.c struct buffer { u8_t *buf; u8_t *readp; u8_t *writep; u8_t *wrap; size_t size; size_t base_size; mutex_type mutex; }; // _* called with mutex locked unsigned _buf_used(struct buffer *buf); unsigned _buf_space(struct buffer *buf); unsigned _buf_cont_read(struct buffer *buf); unsigned _buf_cont_write(struct buffer *buf); void _buf_inc_readp(struct buffer *buf, unsigned by); void _buf_inc_writep(struct buffer *buf, unsigned by); void buf_flush(struct buffer *buf); void _buf_unwrap(struct buffer *buf, size_t cont); void buf_adjust(struct buffer *buf, size_t mod); void _buf_resize(struct buffer *buf, size_t size); void buf_init(struct buffer *buf, size_t size); void buf_destroy(struct buffer *buf); // slimproto.c void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile, const char *modelname, int maxSampleRate); void slimproto_stop(void); void wake_controller(void); // stream.c typedef enum { STOPPED = 0, DISCONNECT, STREAMING_WAIT, STREAMING_BUFFERING, STREAMING_FILE, STREAMING_HTTP, SEND_HEADERS, RECV_HEADERS } stream_state; typedef enum { DISCONNECT_OK = 0, LOCAL_DISCONNECT = 1, REMOTE_DISCONNECT = 2, UNREACHABLE = 3, TIMEOUT = 4 } disconnect_code; struct streamstate { stream_state state; disconnect_code disconnect; char *header; size_t header_len; bool sent_headers; bool cont_wait; u64_t bytes; unsigned threshold; u32_t meta_interval; u32_t meta_next; u32_t meta_left; bool meta_send; }; void stream_init(log_level level, unsigned stream_buf_size); void stream_close(void); void stream_file(const char *header, size_t header_len, unsigned threshold); void stream_sock(u32_t ip, u16_t port, bool use_ssl, const char *header, size_t header_len, unsigned threshold, bool cont_wait); bool stream_disconnect(void); // decode.c typedef enum { DECODE_STOPPED = 0, DECODE_READY, DECODE_RUNNING, DECODE_COMPLETE, DECODE_ERROR } decode_state; struct decodestate { decode_state state; bool new_stream; mutex_type mutex; #if PROCESS bool direct; bool process; #endif }; #if PROCESS struct processstate { u8_t *inbuf, *outbuf; unsigned max_in_frames, max_out_frames; unsigned in_frames, out_frames; unsigned in_sample_rate, out_sample_rate; unsigned long total_in, total_out; }; #endif struct codec { char id; char *types; unsigned min_read_bytes; unsigned min_space; void (*open)(u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness); void (*close)(void); decode_state (*decode)(void); }; void decode_init(log_level level, const char *include_codecs, const char *exclude_codecs); void decode_close(void); void decode_flush(void); 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); #if PROCESS // process.c void process_samples(void); void process_drain(void); void process_flush(void); unsigned process_newstream(bool *direct, unsigned raw_sample_rate, unsigned supported_rates[]); void process_init(char *opt); #endif #if RESAMPLE // resample.c void resample_samples(struct processstate *process); bool resample_drain(struct processstate *process); bool resample_newstream(struct processstate *process, unsigned raw_sample_rate, unsigned supported_rates[]); void resample_flush(void); bool resample_init(char *opt); #endif // output.c output_alsa.c output_pa.c output_pack.c typedef enum { OUTPUT_OFF = -1, OUTPUT_STOPPED = 0, OUTPUT_BUFFER, OUTPUT_RUNNING, OUTPUT_PAUSE_FRAMES, OUTPUT_SKIP_FRAMES, OUTPUT_START_AT } output_state; #if DSD typedef enum { PCM, DOP, DSD_U8, DSD_U16_LE, DSD_U32_LE, DSD_U16_BE, DSD_U32_BE, DOP_S24_LE, DOP_S24_3LE } dsd_format; typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE, U8, U16_LE, U16_BE, U32_LE, U32_BE } output_format; #else typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE } output_format; #endif typedef enum { FADE_INACTIVE = 0, FADE_DUE, FADE_ACTIVE } fade_state; typedef enum { FADE_UP = 1, FADE_DOWN, FADE_CROSS } fade_dir; typedef enum { FADE_NONE = 0, FADE_CROSSFADE, FADE_IN, FADE_OUT, FADE_INOUT } fade_mode; #define MONO_RIGHT 0x02 #define MONO_LEFT 0x01 #define MAX_SUPPORTED_SAMPLERATES 20 #define TEST_RATES = { 1536000, 1411200, 768000, 705600, 384000, 352800, 192000, 176400, 96000, 88200, 48000, 44100, 32000, 24000, 22500, 16000, 12000, 11025, 8000, 0 } struct outputstate { output_state state; output_format format; u8_t channels; const char *device; #if ALSA unsigned buffer; unsigned period; #endif bool track_started; #if PORTAUDIO bool pa_reopen; unsigned latency; int pa_hostapi_option; #endif int (* write_cb)(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, u8_t flags, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr); unsigned start_frames; unsigned frames_played; unsigned frames_played_dmp;// frames played at the point delay is measured unsigned current_sample_rate; unsigned supported_rates[MAX_SUPPORTED_SAMPLERATES]; // ordered largest first so [0] is max_rate unsigned default_sample_rate; bool error_opening; unsigned device_frames; u32_t updated; u32_t track_start_time; u32_t current_replay_gain; union { u32_t pause_frames; u32_t skip_frames; u32_t start_at; }; unsigned next_sample_rate; // set in decode thread u8_t *track_start; // set in decode thread u32_t gainL; // set by slimproto u32_t gainR; // set by slimproto bool invert; // set by slimproto u32_t next_replay_gain; // set by slimproto unsigned threshold; // set by slimproto fade_state fade; u8_t *fade_start; u8_t *fade_end; fade_dir fade_dir; fade_mode fade_mode; // set by slimproto unsigned fade_secs; // set by slimproto unsigned rate_delay; bool delay_active; u32_t stop_time; u32_t idle_to; #if DSD dsd_format next_fmt; // set in decode thread dsd_format outfmt; dsd_format dsdfmt; // set in dsd_init - output for DSD: DOP, DSD_U8, ... unsigned dsd_delay; // set in dsd_init - delay in ms switching to/from dop #endif }; void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[], unsigned idle); void output_close_common(void); void output_flush(void); // _* called with mutex locked frames_t _output_frames(frames_t avail); void _checkfade(bool); // output_alsa.c #if ALSA void list_devices(void); void list_mixers(const char *output_device); void set_volume(unsigned left, unsigned right); bool test_open(const char *device, unsigned rates[], bool userdef_rates); void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned rt_priority, unsigned idle, char *mixer_device, char *volume_mixer, bool mixer_unmute, bool mixer_linear); void output_close_alsa(void); #endif // output_pa.c #if PORTAUDIO void list_devices(void); void set_volume(unsigned left, unsigned right); bool test_open(const char *device, unsigned rates[], bool userdef_rates); void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle); void output_close_pa(void); void _pa_open(void); #endif // output_pulse.c #if PULSEAUDIO void list_devices(void); void set_volume(unsigned left, unsigned right); void set_sample_rate(uint32_t sample_rate); bool test_open(const char *device, unsigned rates[], bool userdef_rates); void output_init_pulse(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle); void output_close_pulse(void); #endif // output_stdout.c void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay); void output_close_stdout(void); // output_pack.c void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, u8_t flags, output_format format); void _apply_cross(struct buffer *outputbuf, frames_t out_frames, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr); void _apply_gain(struct buffer *outputbuf, frames_t count, s32_t gainL, s32_t gainR, u8_t flags); s32_t gain(s32_t gain, s32_t sample); s32_t to_gain(float f); // output_vis.c #if VISEXPORT void _vis_export(struct buffer *outputbuf, struct outputstate *output, frames_t out_frames, bool silence); void output_vis_init(log_level level, u8_t *mac); void vis_stop(void); #else #define _vis_export(...) #define vis_stop() #endif // dop.c #if DSD bool is_stream_dop(u8_t *lptr, u8_t *rptr, int step, frames_t frames); void update_dop(u32_t *ptr, frames_t frames, bool invert); void dsd_silence_frames(u32_t *ptr, frames_t frames); void dsd_invert(u32_t *ptr, frames_t frames); void dsd_init(dsd_format format, unsigned delay); #endif // codecs #define MAX_CODECS 10 struct codec *register_flac(void); struct codec *register_pcm(void); struct codec *register_mad(void); struct codec *register_mpg(void); struct codec *register_vorbis(void); #if ALAC struct codec *register_alac(void); #endif struct codec *register_faad(void); struct codec *register_dsd(void); struct codec *register_ff(const char *codec); #if OPUS struct codec *register_opus(void); #endif // gpio.c #if GPIO void relay(int state); void relay_script(int state); int gpio_pin; bool gpio_active_low; bool gpio_active; char *power_script; #if RPI #define PI_INPUT 0 #define PI_OUTPUT 1 #define PI_LOW 0 #define PI_HIGH 1 void gpioSetMode(unsigned gpio, unsigned mode); void gpioWrite(unsigned gpio, unsigned level); int gpioInitialise(void); #endif #endif // ir.c #if IR struct irstate { mutex_type mutex; u32_t code; u32_t ts; }; void ir_init(log_level level, char *lircrc); void ir_close(void); #endif // sslsym.c #if USE_SSL && !LINKALL && !NO_SSLSYM bool load_ssl_symbols(void); void free_ssl_symbols(void); bool ssl_loaded; #endif squeezelite-1.9.9-1395+git20220104.874e4f9/squeezelite.sln000077500000000000000000000015731416534420200221620ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 10.00 # Visual Studio 2008 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "squeezelite", "squeezelite.vcproj", "{7CB5A216-CEFA-4932-93CD-E9BFA33DD349}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|Win32.ActiveCfg = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Debug|Win32.Build.0 = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|Win32.ActiveCfg = Release|Win32 {7CB5A216-CEFA-4932-93CD-E9BFA33DD349}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal squeezelite-1.9.9-1395+git20220104.874e4f9/squeezelite.vcproj000077500000000000000000000165401416534420200226710ustar00rootroot00000000000000 squeezelite-1.9.9-1395+git20220104.874e4f9/sslsym.c000066400000000000000000000121651416534420200205770ustar00rootroot00000000000000/* * SSL symbols dynamic loader * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #if USE_SSL && !LINKALL && !NO_SSLSYM #if WIN #define dlclose FreeLibrary #else #include #endif #include "openssl/ssl.h" #include "openssl/err.h" #include #include #include #include #include static void *SSLhandle = NULL; static void *CRYPThandle = NULL; #if WIN static char *LIBSSL[] = { "libssl.dll", "ssleay32.dll", NULL }; static char *LIBCRYPTO[] = { "libcrypto.dll", "libeay32.dll", NULL }; #elif OSX static char *LIBSSL[] = { "libssl.dylib", NULL }; static char *LIBCRYPTO[] = { "libcrypto.dylib", NULL }; #else static char *LIBSSL[] = { "libssl.so", "libssl.so.1.1", "libssl.so.1.0.2", "libssl.so.1.0.1", "libssl.so.1.0.0", NULL }; static char *LIBCRYPTO[] = { "libcrypto.so", "libcrypto.so.1.1", "libcrypto.so.1.0.2", "libcrypto.so.1.0.1", "libcrypto.so.1.0.0", NULL }; #endif #define P0() void #define P1(t1, p1) t1 p1 #define P2(t1, p1, t2, p2) t1 p1, t2 p2 #define P3(t1, p1, t2, p2, t3, p3) t1 p1, t2 p2, t3 p3 #define P4(t1, p1, t2, p2, t3, p3, t4, p4) t1 p1, t2 p2, t3 p3, t4 p4 #define P5(t1, p1, t2, p2, t3, p3, t4, p4, t5, p5) t1 p1, t2 p2, t3 p3, t4 p4, t5 p5 #define P6(t1, p1, t2, p2, t3, p3, t4, p4, t5, p5, t6, p6) t1 p1, t2 p2, t3 p3, t4 p4, t5 p5, t6 p6 #define V0() #define V1(t1, p1) p1 #define V2(t1, p1, t2, p2) p1, p2 #define V3(t1, p1, t2, p2, t3, p3) p1, p2, p3 #define V4(t1, p1, t2, p2, t3, p3, t4, p4) p1, p2, p3, p4 #define V5(t1, p1, t2, p2, t3, p3, t4, p4, t5, p5) p1, p2, p3, p4, p5 #define V6(t1, p1, t2, p2, t3, p3, t4, p4, t5, p5, t6, p6) p1, p2, p3, p4, p5, p6 #define P(n, ...) P##n(__VA_ARGS__) #define V(n, ...) V##n(__VA_ARGS__) #define SYM(fn) dlsym_##fn #define SYMDECL(fn, ret, n, ...) \ static ret (*dlsym_##fn)(P(n,__VA_ARGS__)); \ ret fn(P(n,__VA_ARGS__)) { \ return (*dlsym_##fn)(V(n,__VA_ARGS__)); \ } #define SYMDECLVOID(fn, n, ...) \ static void (*dlsym_##fn)(P(n,__VA_ARGS__)); \ void fn(P(n,__VA_ARGS__)) { \ (*dlsym_##fn)(V(n,__VA_ARGS__)); \ } #define SYMLOAD(h, fn) dlsym_##fn = dlsym(h, #fn) SYMDECL(SSL_read, int, 3, SSL*, s, void*, buf, int, len); SYMDECL(SSL_write, int, 3, SSL*, s, const void*, buf, int, len); SYMDECL(SSLv23_client_method, const SSL_METHOD*, 0); SYMDECL(TLS_client_method, const SSL_METHOD*, 0); SYMDECL(SSL_library_init, int, 0); SYMDECL(SSL_CTX_new, SSL_CTX*, 1, const SSL_METHOD *, meth); SYMDECL(SSL_CTX_ctrl, long, 4, SSL_CTX *, ctx, int, cmd, long, larg, void*, parg); SYMDECL(SSL_new, SSL*, 1, SSL_CTX*, s); SYMDECL(SSL_connect, int, 1, SSL*, s); SYMDECL(SSL_shutdown, int, 1, SSL*, s); SYMDECL(SSL_get_fd, int, 1, const SSL*, s); SYMDECL(SSL_set_fd, int, 2, SSL*, s, int, fd); SYMDECL(SSL_get_error, int, 2, const SSL*, s, int, ret_code); SYMDECL(SSL_ctrl, long, 4, SSL*, ssl, int, cmd, long, larg, void*, parg); SYMDECL(SSL_pending, int, 1, const SSL*, s); SYMDECLVOID(SSL_free, 1, SSL*, s); SYMDECLVOID(SSL_CTX_free, 1, SSL_CTX *, ctx); SYMDECL(ERR_get_error, unsigned long, 0); SYMDECLVOID(ERR_clear_error, 0); static void *dlopen_try(char **filenames, int flag) { void *handle; for (handle = NULL; !handle && *filenames; filenames++) handle = dlopen(*filenames, flag); return handle; } static int lambda(void) { return true; } bool load_ssl_symbols(void) { CRYPThandle = dlopen_try(LIBCRYPTO, RTLD_NOW); SSLhandle = dlopen_try(LIBSSL, RTLD_NOW); if (!SSLhandle || !CRYPThandle) { free_ssl_symbols(); return false; } SYMLOAD(SSLhandle, SSL_CTX_new); SYMLOAD(SSLhandle, SSL_CTX_ctrl); SYMLOAD(SSLhandle, SSL_CTX_free); SYMLOAD(SSLhandle, SSL_ctrl); SYMLOAD(SSLhandle, SSL_free); SYMLOAD(SSLhandle, SSL_new); SYMLOAD(SSLhandle, SSL_connect); SYMLOAD(SSLhandle, SSL_get_fd); SYMLOAD(SSLhandle, SSL_set_fd); SYMLOAD(SSLhandle, SSL_get_error); SYMLOAD(SSLhandle, SSL_shutdown); SYMLOAD(SSLhandle, SSL_read); SYMLOAD(SSLhandle, SSL_write); SYMLOAD(SSLhandle, SSL_pending); SYMLOAD(SSLhandle, SSLv23_client_method); SYMLOAD(SSLhandle, TLS_client_method); SYMLOAD(SSLhandle, SSL_library_init); SYMLOAD(CRYPThandle, ERR_clear_error); SYMLOAD(CRYPThandle, ERR_get_error); // managed deprecated functions if (!SYM(SSLv23_client_method)) SYM(SSLv23_client_method) = SYM(TLS_client_method); if (!SYM(SSL_library_init)) SYM(SSL_library_init) = λ return true; } void free_ssl_symbols(void) { if (SSLhandle) dlclose(SSLhandle); if (CRYPThandle) dlclose(CRYPThandle); } #endif squeezelite-1.9.9-1395+git20220104.874e4f9/stream.c000066400000000000000000000340511416534420200205360ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ // stream thread #define _GNU_SOURCE #include "squeezelite.h" #include #if USE_SSL #include "openssl/ssl.h" #include "openssl/err.h" #endif #if SUN #include #endif static log_level loglevel; static struct buffer buf; struct buffer *streambuf = &buf; #define LOCK mutex_lock(streambuf->mutex) #define UNLOCK mutex_unlock(streambuf->mutex) static sockfd fd; static struct sockaddr_in addr; static char host[256]; static int header_mlen; struct streamstate stream; #if USE_SSL #define _last_error() ERROR_WOULDBLOCK static SSL_CTX *SSLctx; SSL *ssl; static int _recv(SSL *ssl, int fd, void *buffer, size_t bytes, int options) { int n; if (!ssl) return recv(fd, buffer, bytes, options); n = SSL_read(ssl, (u8_t*) buffer, bytes); if (n <= 0 && SSL_get_error(ssl, n) == SSL_ERROR_ZERO_RETURN) return 0; return n; } static int _send(SSL *ssl, int fd, void *buffer, size_t bytes, int options) { int n; if (!ssl) return send(fd, buffer, bytes, options); while (1) { int err; ERR_clear_error(); if ((n = SSL_write(ssl, (u8_t*) buffer, bytes)) >= 0) return n; err = SSL_get_error(ssl, n); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) continue; LOG_INFO("SSL write error %d", err ); return n; } } /* can't mimic exactly poll as SSL is a real pain. Even if SSL_pending returns 0, there might be bytes to read but when select (poll) return > 0, there might be no frame available. As well select (poll) < 0 does not mean that there is no data pending */ static int _poll(SSL *ssl, struct pollfd *pollinfo, int timeout) { if (!ssl) return poll(pollinfo, 1, timeout); if (pollinfo->events & POLLIN && SSL_pending(ssl)) { if (pollinfo->events & POLLOUT) poll(pollinfo, 1, 0); pollinfo->revents = POLLIN; return 1; } return poll(pollinfo, 1, timeout); } #else #define _recv(ssl, fc, buf, n, opt) recv(fd, buf, n, opt) #define _send(ssl, fd, buf, n, opt) send(fd, buf, n, opt) #define _poll(ssl, pollinfo, timeout) poll(pollinfo, 1, timeout) #define _last_error() last_error() #endif // USE_SSL static bool send_header(void) { char *ptr = stream.header; int len = stream.header_len; unsigned try = 0; ssize_t n; int error; while (len) { n = _send(ssl, fd, ptr, len, MSG_NOSIGNAL); if (n <= 0) { error = _last_error(); #if WIN if (n < 0 && (error == ERROR_WOULDBLOCK || error == WSAENOTCONN) && try < 10) { #else if (n < 0 && error == ERROR_WOULDBLOCK && try < 10) { #endif LOG_DEBUG("retrying (%d) writing to socket", ++try); usleep(1000); continue; } LOG_WARN("failed writing to socket: %s", strerror(last_error())); stream.disconnect = LOCAL_DISCONNECT; stream.state = DISCONNECT; wake_controller(); return false; } LOG_SDEBUG("wrote %d bytes to socket", n); ptr += n; len -= n; } LOG_SDEBUG("wrote header"); return true; } static bool running = true; static void _disconnect(stream_state state, disconnect_code disconnect) { stream.state = state; stream.disconnect = disconnect; #if USE_SSL if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); ssl = NULL; } #endif closesocket(fd); fd = -1; wake_controller(); } static int connect_socket(bool use_ssl) { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { LOG_ERROR("failed to create socket"); return -1; } LOG_INFO("connecting to %s:%d", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); set_nonblock(sock); set_nosigpipe(sock); if (connect_timeout(sock, (struct sockaddr *) &addr, sizeof(addr), 10) < 0) { LOG_INFO("unable to connect to server"); closesocket(sock); return -1; } #if USE_SSL if (use_ssl) { ssl = SSL_new(SSLctx); SSL_set_fd(ssl, sock); // add SNI if (*host) SSL_set_tlsext_host_name(ssl, host); while (1) { int status, err = 0; ERR_clear_error(); status = SSL_connect(ssl); // successful negotiation if (status == 1) break; // error or non-blocking requires more time if (status < 0) { err = SSL_get_error(ssl, status); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { usleep(1000); continue; } } LOG_WARN("unable to open SSL socket %d (%d)", status, err); closesocket(sock); SSL_free(ssl); ssl = NULL; return -1; } } #endif return sock; } static void *stream_thread() { while (running) { struct pollfd pollinfo; size_t space; LOCK; space = min(_buf_space(streambuf), _buf_cont_write(streambuf)); if (fd < 0 || !space || stream.state <= STREAMING_WAIT) { UNLOCK; usleep(100000); continue; } if (stream.state == STREAMING_FILE) { int n = read(fd, streambuf->writep, space); if (n == 0) { LOG_INFO("end of stream"); _disconnect(DISCONNECT, DISCONNECT_OK); } if (n > 0) { _buf_inc_writep(streambuf, n); stream.bytes += n; LOG_SDEBUG("streambuf read %d bytes", n); } if (n < 0) { LOG_WARN("error reading: %s", strerror(last_error())); _disconnect(DISCONNECT, REMOTE_DISCONNECT); } UNLOCK; continue; } else { pollinfo.fd = fd; pollinfo.events = POLLIN; if (stream.state == SEND_HEADERS) { pollinfo.events |= POLLOUT; } } UNLOCK; if (_poll(ssl, &pollinfo, 100)) { LOCK; // check socket has not been closed while in poll if (fd < 0) { UNLOCK; continue; } if ((pollinfo.revents & POLLOUT) && stream.state == SEND_HEADERS) { if (send_header()) stream.state = RECV_HEADERS; header_mlen = stream.header_len; stream.header_len = 0; UNLOCK; continue; } if (pollinfo.revents & (POLLIN | POLLHUP)) { // get response headers if (stream.state == RECV_HEADERS) { // read one byte at a time to catch end of header char c; static int endtok; int n = _recv(ssl, fd, &c, 1, 0); if (n <= 0) { if (n < 0 && _last_error() == ERROR_WOULDBLOCK) { UNLOCK; continue; } LOG_INFO("error reading headers: %s", n ? strerror(last_error()) : "closed"); #if USE_SSL if (!ssl && !stream.header_len) { int sock; closesocket(fd); fd = -1; stream.header_len = header_mlen; LOG_INFO("now attempting with SSL"); // must be performed locked in case slimproto sends a disconnects sock = connect_socket(true); if (sock >= 0) { fd = sock; stream.state = SEND_HEADERS; UNLOCK; continue; } } #endif _disconnect(STOPPED, LOCAL_DISCONNECT); UNLOCK; continue; } *(stream.header + stream.header_len) = c; stream.header_len++; if (stream.header_len > MAX_HEADER - 1) { LOG_ERROR("received headers too long: %u", stream.header_len); _disconnect(DISCONNECT, LOCAL_DISCONNECT); } if (stream.header_len > 1 && (c == '\r' || c == '\n')) { endtok++; if (endtok == 4) { *(stream.header + stream.header_len) = '\0'; LOG_INFO("headers: len: %d\n%s", stream.header_len, stream.header); stream.state = stream.cont_wait ? STREAMING_WAIT : STREAMING_BUFFERING; wake_controller(); } } else { endtok = 0; } UNLOCK; continue; } // receive icy meta data if (stream.meta_interval && stream.meta_next == 0) { if (stream.meta_left == 0) { // read meta length u8_t c; int n = _recv(ssl, fd, &c, 1, 0); if (n <= 0) { if (n < 0 && _last_error() == ERROR_WOULDBLOCK) { UNLOCK; continue; } LOG_INFO("error reading icy meta: %s", n ? strerror(last_error()) : "closed"); _disconnect(STOPPED, LOCAL_DISCONNECT); UNLOCK; continue; } stream.meta_left = 16 * c; stream.header_len = 0; // amount of received meta data // MAX_HEADER must be more than meta max of 16 * 255 } if (stream.meta_left) { int n = _recv(ssl, fd, stream.header + stream.header_len, stream.meta_left, 0); if (n <= 0) { if (n < 0 && _last_error() == ERROR_WOULDBLOCK) { UNLOCK; continue; } LOG_INFO("error reading icy meta: %s", n ? strerror(last_error()) : "closed"); _disconnect(STOPPED, LOCAL_DISCONNECT); UNLOCK; continue; } stream.meta_left -= n; stream.header_len += n; } if (stream.meta_left == 0) { if (stream.header_len) { *(stream.header + stream.header_len) = '\0'; LOG_INFO("icy meta: len: %u\n%s", stream.header_len, stream.header); stream.meta_send = true; wake_controller(); } stream.meta_next = stream.meta_interval; UNLOCK; continue; } // stream body into streambuf } else { int n; space = min(_buf_space(streambuf), _buf_cont_write(streambuf)); if (stream.meta_interval) { space = min(space, stream.meta_next); } n = _recv(ssl, fd, streambuf->writep, space, 0); if (n == 0) { LOG_INFO("end of stream (%u bytes)", stream.bytes); _disconnect(DISCONNECT, DISCONNECT_OK); } if (n < 0 && _last_error() != ERROR_WOULDBLOCK) { LOG_INFO("error reading: %s", strerror(last_error())); _disconnect(DISCONNECT, REMOTE_DISCONNECT); } if (n > 0) { _buf_inc_writep(streambuf, n); stream.bytes += n; if (stream.meta_interval) { stream.meta_next -= n; } } else { UNLOCK; continue; } if (stream.state == STREAMING_BUFFERING && stream.bytes > stream.threshold) { stream.state = STREAMING_HTTP; wake_controller(); } LOG_SDEBUG("streambuf read %d bytes", n); } } UNLOCK; } else { LOG_SDEBUG("poll timeout"); } } #if USE_SSL if (SSLctx) { SSL_CTX_free(SSLctx); } #endif return 0; } static thread_type thread; void stream_init(log_level level, unsigned stream_buf_size) { loglevel = level; LOG_INFO("init stream"); LOG_DEBUG("streambuf size: %u", stream_buf_size); buf_init(streambuf, stream_buf_size); if (streambuf->buf == NULL) { LOG_ERROR("unable to malloc buffer"); exit(0); } #if USE_SSL #if !LINKALL && !NO_SSLSYM if (ssl_loaded) { #endif SSL_library_init(); SSLctx = SSL_CTX_new(SSLv23_client_method()); if (SSLctx == NULL) { LOG_ERROR("unable to allocate SSL context"); exit(0); } SSL_CTX_set_options(SSLctx, SSL_OP_NO_SSLv2); #if !LINKALL && !NO_SSLSYM } #endif ssl = NULL; #endif #if SUN signal(SIGPIPE, SIG_IGN); /* Force sockets to return -1 with EPIPE on pipe signal */ #endif stream.state = STOPPED; stream.header = malloc(MAX_HEADER); *stream.header = '\0'; fd = -1; #if LINUX || FREEBSD touch_memory(streambuf->buf, streambuf->size); #endif #if LINUX || OSX || FREEBSD pthread_attr_t attr; pthread_attr_init(&attr); #ifdef PTHREAD_STACK_MIN pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + STREAM_THREAD_STACK_SIZE); #endif pthread_create(&thread, &attr, stream_thread, NULL); pthread_attr_destroy(&attr); #endif #if WIN thread = CreateThread(NULL, STREAM_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&stream_thread, NULL, 0, NULL); #endif } void stream_close(void) { LOG_INFO("close stream"); LOCK; running = false; UNLOCK; #if LINUX || OSX || FREEBSD pthread_join(thread, NULL); #endif free(stream.header); buf_destroy(streambuf); } void stream_file(const char *header, size_t header_len, unsigned threshold) { buf_flush(streambuf); LOCK; stream.header_len = header_len; memcpy(stream.header, header, header_len); *(stream.header+header_len) = '\0'; LOG_INFO("opening local file: %s", stream.header); #if WIN fd = open(stream.header, O_RDONLY | O_BINARY); #else fd = open(stream.header, O_RDONLY); #endif stream.state = STREAMING_FILE; if (fd < 0) { LOG_INFO("can't open file: %s", stream.header); stream.state = DISCONNECT; } wake_controller(); stream.cont_wait = false; stream.meta_interval = 0; stream.meta_next = 0; stream.meta_left = 0; stream.meta_send = false; stream.sent_headers = false; stream.bytes = 0; stream.threshold = threshold; UNLOCK; } void stream_sock(u32_t ip, u16_t port, bool use_ssl, const char *header, size_t header_len, unsigned threshold, bool cont_wait) { char *p; int sock; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = ip; addr.sin_port = port; *host = '\0'; p = strcasestr(header,"Host:"); if (p) { sscanf(p, "Host:%255s", host); if ((p = strchr(host, ':')) != NULL) *p = '\0'; } port = ntohs(port); sock = connect_socket(use_ssl || port == 443); // try one more time with plain socket if (sock < 0 && port == 443 && !use_ssl) sock = connect_socket(false); if (sock < 0) { LOCK; stream.state = DISCONNECT; stream.disconnect = UNREACHABLE; UNLOCK; return; } buf_flush(streambuf); LOCK; fd = sock; stream.state = SEND_HEADERS; stream.cont_wait = cont_wait; stream.meta_interval = 0; stream.meta_next = 0; stream.meta_left = 0; stream.meta_send = false; stream.header_len = header_len; memcpy(stream.header, header, header_len); *(stream.header+header_len) = '\0'; LOG_INFO("header: %s", stream.header); stream.sent_headers = false; stream.bytes = 0; stream.threshold = threshold; UNLOCK; } bool stream_disconnect(void) { bool disc = false; LOCK; #if USE_SSL if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); ssl = NULL; } #endif if (fd != -1) { closesocket(fd); fd = -1; disc = true; } stream.state = STOPPED; UNLOCK; return disc; } squeezelite-1.9.9-1395+git20220104.874e4f9/tools/000077500000000000000000000000001416534420200202345ustar00rootroot00000000000000squeezelite-1.9.9-1395+git20220104.874e4f9/tools/alsacap.c000066400000000000000000000510231416534420200220050ustar00rootroot00000000000000/* * ALSA parameter test program * * Copyright (c) 2007 Volker Schatz (alsacap at the domain volkerschatz.com) * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * * This program was originally written by Volker Schatz. Shawn Wilson * bundled it into an autotools package and cut and pasted some code * from the alsa speaker-test command to display min/max buffer and * period sizes. * */ /*============================================================================ Includes ============================================================================*/ #include #include #include #include #include #include /*============================================================================ Constant and type definitions ============================================================================*/ #define RATE_KHZ_LIMIT 200 #define HWP_END 0 #define HWP_RATE 1 #define HWP_NCH 2 #define HWP_FORMAT 3 #define SIZE_HWP 7 typedef struct { int recdevices, verbose, card, dev; char *device; int hwparams[SIZE_HWP]; } aiopts; /*============================================================================ Global variables ============================================================================*/ static snd_ctl_t *handle= NULL; static snd_pcm_t *pcm= NULL; static snd_ctl_card_info_t *info; static snd_pcm_info_t *pcminfo; static snd_pcm_hw_params_t *pars; static snd_pcm_format_mask_t *fmask; /*============================================================================ Prototypes ============================================================================*/ void usagemsg(int code); void errnumarg(char optchar); void errarg(char optchar); void errtoomany(); void scancards(snd_pcm_stream_t stream, int thecard, int thedev); int sc_errcheck(int retval, const char *doingwhat, int cardnr, int devnr); void testconfig(snd_pcm_stream_t stream, const char *device, const int *hwpars); void tc_errcheck(int retval, const char *doingwhat); const char *alsaerrstr(const int errcode); const char *dirstr(int dir); int parse_alsaformat(const char *fmtstr); const char *alsafmtstr(int fmtnum); void printfmtmask(const snd_pcm_format_mask_t *fmask); /*============================================================================ Main program ============================================================================*/ int main(int argc, char **argv) { aiopts options= { 0, 1, -1, -1, NULL }; snd_pcm_stream_t stream; char *argpar; int argind, hwpind; snd_ctl_card_info_alloca(&info); snd_pcm_info_alloca(&pcminfo); snd_pcm_hw_params_alloca(&pars); snd_pcm_format_mask_alloca(&fmask); hwpind= 0; for( argind= 1; argind< argc; ++argind ) { if( argv[argind][0]!='-' ) { fprintf(stderr, "Unrecognised command-line argument `%s'.\n", argv[argind]); usagemsg(1); } if( argv[argind][2] ) argpar= argv[argind]+2; else { if( argind+1 >= argc ) argpar= NULL; else argpar= argv[argind+1]; } if( argv[argind][1]=='h' || !strcmp(argv[argind]+1, "-help") ) usagemsg(0); else if( argv[argind][1]=='R' ) { options.recdevices= 1; argpar= NULL; // set to NULL if unused to keep track of next arg index } else if( argv[argind][1]=='C' ) { if( !argpar || !isdigit(*argpar) ) errnumarg('C'); options.card= strtol(argpar, NULL, 0); } else if( argv[argind][1]=='D' ) { if( !argpar || !isdigit(*argpar) ) errnumarg('D'); options.dev= strtol(argpar, NULL, 0); } else if( argv[argind][1]=='d' ) { if( !argpar ) errarg('d'); options.device= argpar; } else if( argv[argind][1]=='r' ) { if( !argpar || !isdigit(*argpar) ) errnumarg('r'); if( hwpind+3 > SIZE_HWP ) errtoomany(); options.hwparams[hwpind++]= HWP_RATE; options.hwparams[hwpind]= strtol(argpar, NULL, 0); if( options.hwparams[hwpind] <= RATE_KHZ_LIMIT ) options.hwparams[hwpind] *= 1000; // sanity check: Hz or kHz ? ++hwpind; } else if( argv[argind][1]=='c' ) { if( !argpar || !isdigit(*argpar) ) errnumarg('c'); if( hwpind+3 > SIZE_HWP ) errtoomany(); options.hwparams[hwpind++]= HWP_NCH; options.hwparams[hwpind++]= strtol(argpar, NULL, 0); } else if( argv[argind][1]=='f' ) { if( !argpar ) errarg('f'); if( hwpind+3 > SIZE_HWP ) errtoomany(); options.hwparams[hwpind++]= HWP_FORMAT; options.hwparams[hwpind++]= parse_alsaformat(argpar); } else { fprintf(stderr, "Unrecognised command-line option `%s'.\n", argv[argind]); usagemsg(1); } if( argpar && !argv[argind][2] ) ++argind; // additional increment if separate parameter argument was used } options.hwparams[hwpind]= HWP_END; if( options.dev >= 0 && options.card < 0 ) { fprintf(stderr, "The card has to be specified with -C if a device number is given (-D).\n"); exit(1); } if( options.device && (options.card>=0 || options.dev>=0) ) { fprintf(stderr, "Specifying a device name (-d) and a card and possibly device number (-C, -D) is mutually exclusive.\n"); exit(1); } stream= options.recdevices? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK; if( !options.device ) scancards(stream, options.card, options.dev); else testconfig(stream, options.device, options.hwparams); } /*============================================================================ Usage message and command-line argument error functions ============================================================================*/ void usagemsg(int code) { fprintf(stderr, "Usage: alsacap [-R] [-C [-D ]]\n" " alsacap [-R] -d [-r |-c <# of channels>|-f ]...\n" "ALSA capability lister.\n" "First form: Scans one or all soundcards known to ALSA for devices, \n" "subdevices and parameter ranges. -R causes a scan for recording\n" "rather than playback devices. The other options specify the sound\n" "card and possibly the device by number.\n" "Second form: Displays ranges of configuration parameters for the given\n" "ALSA device. Unlike with the first form, a non-hardware device may be\n" "given. Up to three optional command-line arguments fix the rate,\n" "number of channels and sample format in the order in which they are\n" "given. The remaining parameter ranges are output. If unique, the\n" "number of significant bits of the sample values is output. (Some\n" "sound cards ignore some of the bits.)\n"); exit(code); } void errnumarg(char optchar) { fprintf(stderr, "The -%c option requires a numerical argument! Aborting.\n", optchar); exit(1); } void errarg(char optchar) { fprintf(stderr, "The -%c option requires an argument! Aborting.\n", optchar); exit(1); } void errtoomany() { fprintf(stderr, "Too many -r/-c/-f options given! (Maximum is %d.) Aborting.\n", (SIZE_HWP-1)/2); exit(1); } /*============================================================================ Function for scanning all cards ============================================================================*/ #define HWCARDTEMPL "hw:%d" #define HWDEVTEMPL "hw:%d,%d" #define HWDEVLEN 32 void scancards(snd_pcm_stream_t stream, int thecard, int thedev) { char hwdev[HWDEVLEN+1]; unsigned min, max; int card, err, dev, subd, nsubd; snd_pcm_uframes_t period_size_min; snd_pcm_uframes_t period_size_max; snd_pcm_uframes_t buffer_size_min; snd_pcm_uframes_t buffer_size_max; printf("*** Scanning for %s devices", stream == SND_PCM_STREAM_CAPTURE? "recording" : "playback"); if( thecard >= 0 ) printf(" on card %d", thecard); if( thedev >= 0 ) printf(", device %d", thedev); printf(" ***\n"); hwdev[HWDEVLEN]= 0; if( thecard >= 0 ) card= thecard; else { card= -1; if( snd_card_next(&card) < 0 ) return; } while( card >= 0 ) { snprintf(hwdev, HWDEVLEN, HWCARDTEMPL, card); err= snd_ctl_open(&handle, hwdev, 0); if( sc_errcheck(err, "opening control interface", card, -1) ) goto nextcard; err= snd_ctl_card_info(handle, info); if( sc_errcheck(err, "obtaining card info", card, -1) ) { snd_ctl_close(handle); goto nextcard; } printf("\nCard %d, ID `%s', name `%s'\n", card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info)); if( thedev >= 0 ) dev= thedev; else { dev= -1; if( snd_ctl_pcm_next_device(handle, &dev) < 0 ) { snd_ctl_close(handle); goto nextcard; } } while( dev >= 0 ) { snd_pcm_info_set_device(pcminfo, dev); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); err= snd_ctl_pcm_info(handle, pcminfo); if( thedev<0 && err == -ENOENT ) goto nextdev; if( sc_errcheck(err, "obtaining device info", card, dev) ) goto nextdev; nsubd= snd_pcm_info_get_subdevices_count(pcminfo); if( sc_errcheck(nsubd, "obtaining device info", card, dev) ) goto nextdev; printf( " Device %d, ID `%s', name `%s', %d subdevices (%d available)\n", dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); snprintf(hwdev, HWDEVLEN, HWDEVTEMPL, card, dev); err= snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); if( sc_errcheck(err, "opening sound device", card, dev) ) goto nextdev; err= snd_pcm_hw_params_any(pcm, pars); if( sc_errcheck(err, "obtaining hardware parameters", card, dev) ) { snd_pcm_close(pcm); goto nextdev; } snd_pcm_hw_params_get_channels_min(pars, &min); snd_pcm_hw_params_get_channels_max(pars, &max); if( min == max ) if( min == 1 ) printf(" 1 channel, "); else printf(" %d channels, ", min); else printf(" %u..%u channels, ", min, max); /* Find and print out min/max sampling rates. */ snd_pcm_hw_params_get_rate_min(pars, &min, NULL); snd_pcm_hw_params_get_rate_max(pars, &max, NULL); printf("sampling rate %u..%u Hz\n", min, max); /* Find and print out possible PCM formats. */ snd_pcm_hw_params_get_format_mask(pars, fmask); printf(" Sample formats: "); printfmtmask(fmask); printf("\n"); /* Find and print out min/max buffer and period sizes. */ err = snd_pcm_hw_params_get_buffer_size_min( pars, &buffer_size_min); err = snd_pcm_hw_params_get_buffer_size_max( pars, &buffer_size_max); err = snd_pcm_hw_params_get_period_size_min( pars, &period_size_min, NULL); err = snd_pcm_hw_params_get_period_size_max( pars, &period_size_max, NULL); printf(" Buffer size range from %lu to %lu\n", buffer_size_min, buffer_size_max); printf(" Period size range from %lu to %lu\n", period_size_min, period_size_max); snd_pcm_close(pcm); pcm= NULL; for( subd= 0; subd< nsubd; ++subd ) { snd_pcm_info_set_subdevice(pcminfo, subd); err= snd_ctl_pcm_info(handle, pcminfo); if( sc_errcheck(err, "obtaining subdevice info", card, dev) ) goto nextdev; printf(" Subdevice %d, name `%s'\n", subd, snd_pcm_info_get_subdevice_name(pcminfo)); } nextdev: if( thedev >= 0 || snd_ctl_pcm_next_device(handle, &dev) < 0 ) break; } snd_ctl_close(handle); nextcard: if( thecard >= 0 || snd_card_next(&card) < 0 ) break; } } int sc_errcheck(int retval, const char *doingwhat, int cardnr, int devnr) { if( retval<0 ) { if( devnr>= 0 ) fprintf(stderr, "Error %s for card %d, device %d: %s. Skipping.\n", doingwhat, cardnr, devnr, alsaerrstr(retval)); else fprintf(stderr, "Error %s for card %d: %s. Skipping.\n", doingwhat, cardnr, alsaerrstr(retval)); return 1; } return 0; } /*============================================================================ Function for investigating device configurations ============================================================================*/ void testconfig(snd_pcm_stream_t stream, const char *device, const int *hwpars) { unsigned min, max, param; int err, count, dir, result; snd_pcm_uframes_t period_size_min; snd_pcm_uframes_t period_size_max; snd_pcm_uframes_t buffer_size_min; snd_pcm_uframes_t buffer_size_max; printf("*** Exploring configuration space of device `%s' for %s ***\n", device, stream==SND_PCM_STREAM_CAPTURE? "recording" : "playback"); err= snd_pcm_open(&pcm, device, stream, SND_PCM_NONBLOCK); tc_errcheck(err, "opening sound device"); err= snd_pcm_hw_params_any(pcm, pars); tc_errcheck(err, "initialising hardware parameters"); for( count= 0; hwpars[count]!=HWP_END; count += 2 ) switch(hwpars[count]) { case HWP_RATE:param= hwpars[count+1]; err= snd_pcm_hw_params_set_rate_near(pcm, pars, ¶m, &result); if( err<0 ) fprintf(stderr, "Could not set sampling rate to %d Hz: %s. " "Continuing regardless.\n", hwpars[count+1], alsaerrstr(err)); else printf("Set sampling rate %d Hz --> got %u Hz, %s requested.\n", hwpars[count+1], param, dirstr(dir)); break; case HWP_NCH:err= snd_pcm_hw_params_set_channels(pcm, pars, hwpars[count+1]); if( err<0 ) fprintf(stderr, "Could not set # of channels to %d: %s. " "Continuing regardless.\n", hwpars[count+1], alsaerrstr(err)); else printf("Set number of channels to %d.\n", hwpars[count+1]); break; case HWP_FORMAT:err= snd_pcm_hw_params_set_format(pcm, pars, hwpars[count+1]); if( err<0 ) fprintf(stderr, "Could not set sample format to %s: %s." " Continuing regardless.\n", alsafmtstr(hwpars[count+1]), alsaerrstr(err)); else printf("Set sample format to %s.\n", alsafmtstr(hwpars[count+1])); break; default: break; } if( count>0 ) printf("Parameter ranges remaining after these settings:\n"); snd_pcm_hw_params_get_channels_min(pars, &min); snd_pcm_hw_params_get_channels_max(pars, &max); if( min==max ) if( min==1 ) printf("1 channel\n"); else printf("%u channels\n", min); else printf("%u..%u channels\n", min, max); snd_pcm_hw_params_get_rate_min(pars, &min, NULL); snd_pcm_hw_params_get_rate_max(pars, &max, NULL); if( min==max ) printf("Sampling rate %u Hz\n", min); else printf("Sampling rate %u..%u Hz\n", min, max); /* Find and print out possible PCM formats. */ snd_pcm_hw_params_get_format_mask(pars, fmask); printf(" Sample formats: "); printfmtmask(fmask); printf("\n"); /* Find and print out min/max buffer and period sizes. */ err = snd_pcm_hw_params_get_buffer_size_min( pars, &buffer_size_min); err = snd_pcm_hw_params_get_buffer_size_max( pars, &buffer_size_max); err = snd_pcm_hw_params_get_period_size_min( pars, &period_size_min, NULL); err = snd_pcm_hw_params_get_period_size_max( pars, &period_size_max, NULL); printf(" Buffer size range from %lu to %lu\n", buffer_size_min, buffer_size_max); printf(" Period size range from %lu to %lu\n", period_size_min, period_size_max); result= snd_pcm_hw_params_get_sbits(pars); if( result >= 0 ) // only available if bit width of all formats is the same printf("Significant bits: %d\n", result); snd_pcm_close(pcm); } void tc_errcheck(int retval, const char *doingwhat) { if( retval<0 ) { fprintf(stderr, "Error %s: %s. Aborting.\n", doingwhat, alsaerrstr(retval)); if( pcm ) snd_pcm_close(pcm); exit(1); } } /*============================================================================ String-building functions ============================================================================*/ struct alsaerr { int err; char *msg; }; struct alsaerr aelist[]= { -EBADFD, "PCM device is in a bad state", -EPIPE, "An underrun occurred", -ESTRPIPE, "A suspend event occurred", -ENOTTY, "Hotplug device has been removed", -ENODEV, "Hotplug device has been removed", -ENOENT, "Device does not exist", 0, NULL }; const char *alsaerrstr(const int errcode) { struct alsaerr *search; if( errcode >= 0 ) return "No error"; for( search= aelist; search->msg && search->err!=errcode; ++search); if( search->msg ) return search->msg; else return strerror(-errcode); } const char *dirstr(int dir) { if( !dir ) return "="; else if( dir<0 ) return "<"; else return ">"; } /*============================================================================ Functions for parsing and string output of ALSA sample formats ============================================================================*/ struct fmtdef { char *fmtname; int format; }; static struct fmtdef fmtlist[]= { "S8", SND_PCM_FORMAT_S8, "U8", SND_PCM_FORMAT_U8, "S16_LE", SND_PCM_FORMAT_S16_LE, "S16_BE", SND_PCM_FORMAT_S16_BE, "U16_LE", SND_PCM_FORMAT_U16_LE, "U16_BE", SND_PCM_FORMAT_U16_BE, "S24_LE", SND_PCM_FORMAT_S24_LE, "S24_BE", SND_PCM_FORMAT_S24_BE, "U24_LE", SND_PCM_FORMAT_U24_LE, "U24_BE", SND_PCM_FORMAT_U24_BE, "S32_LE", SND_PCM_FORMAT_S32_LE, "S32_BE", SND_PCM_FORMAT_S32_BE, "U32_LE", SND_PCM_FORMAT_U32_LE, "U32_BE", SND_PCM_FORMAT_U32_BE, "FLOAT_LE", SND_PCM_FORMAT_FLOAT_LE, "FLOAT_BE", SND_PCM_FORMAT_FLOAT_BE, "FLOAT64_LE", SND_PCM_FORMAT_FLOAT64_LE, "FLOAT64_BE", SND_PCM_FORMAT_FLOAT64_BE, "IEC958_SUBFRAME_LE", SND_PCM_FORMAT_IEC958_SUBFRAME_LE, "IEC958_SUBFRAME_BE", SND_PCM_FORMAT_IEC958_SUBFRAME_BE, "MU_LAW", SND_PCM_FORMAT_MU_LAW, "A_LAW", SND_PCM_FORMAT_A_LAW, "IMA_ADPCM", SND_PCM_FORMAT_IMA_ADPCM, "MPEG", SND_PCM_FORMAT_MPEG, "GSM", SND_PCM_FORMAT_GSM, "SPECIAL", SND_PCM_FORMAT_SPECIAL, "S24_3LE", SND_PCM_FORMAT_S24_3LE, "S24_3BE", SND_PCM_FORMAT_S24_3BE, "U24_3LE", SND_PCM_FORMAT_U24_3LE, "U24_3BE", SND_PCM_FORMAT_U24_3BE, "S20_3LE", SND_PCM_FORMAT_S20_3LE, "S20_3BE", SND_PCM_FORMAT_S20_3BE, "U20_3LE", SND_PCM_FORMAT_U20_3LE, "U20_3BE", SND_PCM_FORMAT_U20_3BE, "S18_3LE", SND_PCM_FORMAT_S18_3LE, "S18_3BE", SND_PCM_FORMAT_S18_3BE, "U18_3LE", SND_PCM_FORMAT_U18_3LE, "U18_3BE", SND_PCM_FORMAT_U18_3BE, "S16", SND_PCM_FORMAT_S16, "U16", SND_PCM_FORMAT_U16, "S24", SND_PCM_FORMAT_S24, "U24", SND_PCM_FORMAT_U24, "S32", SND_PCM_FORMAT_S32, "U32", SND_PCM_FORMAT_U32, "FLOAT", SND_PCM_FORMAT_FLOAT, "FLOAT64", SND_PCM_FORMAT_FLOAT64, "IEC958_SUBFRAME", SND_PCM_FORMAT_IEC958_SUBFRAME, NULL, 0 }; int parse_alsaformat(const char *fmtstr) { struct fmtdef *search; for( search= fmtlist; search->fmtname && strcmp(search->fmtname, fmtstr); ++search ); if( !search->fmtname ) { fprintf(stderr, "Unknown sample format `%s'. Aborting.\n", fmtstr); exit(1); } return search->format; } const char *alsafmtstr(int fmtnum) { struct fmtdef *search; for( search= fmtlist; search->fmtname && search->format!=fmtnum; ++search ); if( !search->fmtname ) return "(unknown)"; else return search->fmtname; } /*============================================================================ Printout functions ============================================================================*/ void printfmtmask(const snd_pcm_format_mask_t *fmask) { int fmt, prevformat= 0; for( fmt= 0; fmt <= SND_PCM_FORMAT_LAST; ++fmt ) if( snd_pcm_format_mask_test(fmask, (snd_pcm_format_t)fmt) ) { if( prevformat ) printf(", "); printf("%s", snd_pcm_format_name((snd_pcm_format_t)fmt)); prevformat= 1; } if( !prevformat ) printf("(none)"); } squeezelite-1.9.9-1395+git20220104.874e4f9/tools/find_servers.c000066400000000000000000000165631416534420200231040ustar00rootroot00000000000000/* * SlimProtoLib is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with SlimProtoLib; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #ifdef __WIN32__ #include #include #include "poll.h" #define CLOSESOCKET(s) closesocket(s) #define MSG_DONTWAIT (0) #else #include #include #include #include #include #include #include #include #include #include #define CLOSESOCKET(s) close(s) #endif #define BUF_LENGTH 4096 /* fprintf(stderr, __VA_ARGS__) */ #define DEBUGF(...) #define VDEBUGF(...) #define packN4(ptr, off, v) { ptr[off] = (char)(v >> 24) & 0xFF; ptr[off+1] = (v >> 16) & 0xFF; ptr[off+2] = (v >> 8) & 0xFF; ptr[off+3] = v & 0xFF; } #define packN2(ptr, off, v) { ptr[off] = (char)(v >> 8) & 0xFF; ptr[off+1] = v & 0xFF; } #define packC(ptr, off, v) { ptr[off] = v & 0xFF; } #define packA4(ptr, off, v) { strncpy((char*)(&ptr[off]), v, 4); } #define unpackN4(ptr, off) ((ptr[off] << 24) | (ptr[off+1] << 16) | (ptr[off+2] << 8) | ptr[off+3]) #define unpackN2(ptr, off) ((ptr[off] << 8) | ptr[off+1]) #define unpackC(ptr, off) (ptr[off]) #define bool int #define true 1 #define false 0 #define DISCOVERY_PKTSIZE 1516 #define SLIMPROTO_DISCOVERY "eNAME\0JSON\0" int slimproto_discover(char *server_addr, int server_addr_len, int port, unsigned int *jsonport, bool scan) { int sockfd; int try; char *packet; int pktlen; int pktidx; char *t; unsigned int l; char *v; char *server_name; char *server_json; struct pollfd pollfd; struct sockaddr_in sendaddr; struct sockaddr_in recvaddr; #ifdef __WIN32__ WSADATA info; #endif socklen_t sockaddr_len = sizeof(sendaddr); int broadcast=1; int serveraddr_len = -1; #ifdef __WIN32__ /* Need to initialize winsock if scanning on windows as slimproto_init has not been called */ if ( scan ) { if (WSAStartup(MAKEWORD(1,1), &info) != 0) { fprintf(stderr, "Cannot initialize WinSock"); return -1; } } #endif if((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { perror("sockfd"); return -1; } pollfd.fd = sockfd; pollfd.events = POLLIN; if((setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const void*) &broadcast, sizeof broadcast)) == -1) { perror("setsockopt - SO_BROADCAST"); return -1; } sendaddr.sin_family = AF_INET; sendaddr.sin_port = htons(0); sendaddr.sin_addr.s_addr = INADDR_ANY; memset(sendaddr.sin_zero,'\0',sizeof sendaddr.sin_zero); if(bind(sockfd, (struct sockaddr*) &sendaddr, sizeof sendaddr) == -1) { perror("bind"); return -1; } recvaddr.sin_family = AF_INET; recvaddr.sin_port = htons(port); recvaddr.sin_addr.s_addr = INADDR_BROADCAST; memset(recvaddr.sin_zero,'\0',sizeof recvaddr.sin_zero); packet = malloc ( sizeof ( char ) * DISCOVERY_PKTSIZE ); v = malloc ( sizeof ( char ) * 256 ); t = malloc ( sizeof ( char ) * 256 ); server_name = malloc ( sizeof ( char ) * 256 ); server_json = malloc ( sizeof ( char ) * 256 ); if ( (packet == NULL) || (v == NULL) || (t == NULL) || (server_name == NULL) || (server_json == NULL) ) { perror("malloc"); return -1; } for (try = 0; try < 5; try ++) { if (sendto(sockfd, SLIMPROTO_DISCOVERY, sizeof(SLIMPROTO_DISCOVERY), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr)) == -1) { CLOSESOCKET(sockfd); perror("sendto"); return -1; } DEBUGF("slimproto_discover: discovery packet sent\n"); /* Wait up to 1 second for response */ while (poll(&pollfd, 1, 1000)) { memset(packet,0,sizeof(packet)); pktlen = recvfrom(sockfd, packet, DISCOVERY_PKTSIZE, MSG_DONTWAIT, (struct sockaddr *)&sendaddr, &sockaddr_len); if ( pktlen == -1 ) continue; /* Invalid response packet, try again */ if ( packet[0] != 'E') continue; memset(server_name,0,sizeof(server_name)); memset(server_json,0,sizeof(server_json)); VDEBUGF("slimproto_discover: pktlen:%d\n",pktlen); /* Skip the E */ pktidx = 1; while ( pktidx < (pktlen - 5) ) { strncpy ( t, &packet[pktidx], pktidx + 3 ); t[4] = '\0'; l = (unsigned int) ( packet[pktidx + 4] ); strncpy ( v, &packet[pktidx + 5], pktidx + 4 + l); v[l] = '\0'; pktidx = pktidx + 5 + l; if ( memcmp ( t, "NAME", 4 ) == 0 ) { strncpy ( server_name, v, l ); server_name[l] = '\0'; } else if ( memcmp ( t, "JSON", 4 ) == 0 ) { strncpy ( server_json, v, l ); server_json[l] = '\0'; } VDEBUGF("slimproto_discover: key: %s len: %d value: %s pktidx: %d\n", t, l, v, pktidx); } inet_ntop(AF_INET, &sendaddr.sin_addr.s_addr, server_addr, server_addr_len); *jsonport = (unsigned int) strtoul(server_json, NULL, 10); DEBUGF("slimproto_discover: discovered %s:%u (%s)\n", server_name, *jsonport, server_addr); serveraddr_len = strlen(server_addr); /* Server(s) responded, so don't try again */ try = 5; if ( scan ) printf("%s:%u (%s)\n", server_name, *jsonport, server_addr); else break ; /* Return first server that replied */ } } CLOSESOCKET(sockfd); if ( scan ) { strcpy ( server_addr, "0.0.0.0" ); *jsonport = 0; serveraddr_len = -1; #ifdef __WIN32__ WSACleanup(); #endif } if ( server_json != NULL ) free (server_json); if ( server_name != NULL ) free (server_name); if ( t != NULL ) free (t); if ( v != NULL ) free (v); if ( packet != NULL ) free (packet); DEBUGF("slimproto_discover: end\n"); return serveraddr_len ; } static void license(void) { printf( "\n" "This program is free software: you can redistribute it and/or modify\n" "it under the terms of the GNU General Public License as published by\n" "the Free Software Foundation, either version 3 of the License, or\n" "(at your option) any later version.\n\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n\n" "You should have received a copy of the GNU General Public License\n" "along with this program. If not, see .\n\n" "The source is available from https://github.com/ralph-irving/squeezelite\n" ); } int main(int argc, char **argv) { char slimserver_address[256] = "127.0.0.1"; int port = 3483; unsigned int json; int len ; if (argc > 1) { license(); exit (1); } /* Scan */ len = slimproto_discover(slimserver_address, sizeof (slimserver_address), port, &json, true); VDEBUGF("main: slimproto_discover_scan: address:%s len:%d json:%u\n", slimserver_address, len, json ); return 0; } squeezelite-1.9.9-1395+git20220104.874e4f9/tools/gpiopower.sh000066400000000000000000000022511416534420200226030ustar00rootroot00000000000000#!/bin/sh # Define GPIO GPIO_OUT=18 # Turn on, and turn off functions turn_on() { echo "1" > /sys/class/gpio/gpio$GPIO_OUT/value } turn_off() { echo "0" > /sys/class/gpio/gpio$GPIO_OUT/value } init_gpio_out() { #================================================= ========================== # Initial GPIO OUT setup #--------------------------------------------------------------------------- sudo sh -c 'echo '"$GPIO_OUT"' > /sys/class/gpio/export' # Relay is active low, so this reverses the logic sudo sh -c 'echo "1" > /sys/class/gpio/gpio'"$GPIO_OUT"'/active_low' sudo sh -c 'echo "out" > /sys/class/gpio/gpio'"$GPIO_OUT"'/direction' sudo sh -c 'echo "0" > /sys/class/gpio/gpio'"$GPIO_OUT"'/value' #--------------------------------------------------------------------------- } case "${1}" in # 2 from cmdline is for first initialization commands, this is run once 2) init_gpio_out ;; # 0 from cmdline is for Off Commands 0) turn_off ;; # 1 from cmdline is for On Commands 1) turn_on ;; esac squeezelite-1.9.9-1395+git20220104.874e4f9/tools/lircd.conf000066400000000000000000000043231416534420200222020ustar00rootroot00000000000000# # using lirc-0.9.4(userspace) on Thu Apr 30 10:29:59 2020 # # contributed by Ralph Irving # # brand: Slim Devices # model no. of remote control: Squeezebox3 Touch # devices being controlled by this remote: Squeezelite # begin remote name Slim_Devices_Squeezebox3 bits 16 flags SPACE_ENC|CONST_LENGTH eps 30 aeps 100 header 9100 4416 one 649 1593 zero 647 473 ptrail 649 pre_data_bits 16 pre_data 0x7689 gap 107995 min_repeat 1 suppress_repeat 6 toggle_bit_mask 0x0 begin codes KEY_VOLUMEDOWN 0x00FF KEY_VOLUMEUP 0x807F KEY_REWIND 0xC03F KEY_FORWARD 0xA05F KEY_PAUSE 0x20DF KEY_PLAY 0x10EF KEY_POWER 0x40BF KEY_UP 0xE01F KEY_LEFT 0x906F KEY_RIGHT 0xD02F KEY_DOWN 0xB04F KEY_1 0xF00F KEY_2 0x08F7 KEY_3 0x8877 KEY_4 0x48B7 KEY_5 0xC837 KEY_6 0x28D7 KEY_7 0xA857 KEY_8 0x6897 KEY_9 0xE817 KEY_0 0x9867 KEY_FAVORITES 0x18E7 KEY_SEARCH 0x58A7 KEY_SHUFFLE 0xD827 KEY_MEDIA_REPEAT 0x38C7 KEY_SLEEP 0xB847 KEY_INSERT 0x609F ## Add KEY_BRIGHTNESS_CYCLE 0x04FB ## Brightness KEY_TEXT 0xF807 ## Size KEY_TITLE 0x7887 ## Now Playing KEY_FAVORITES 0xE21D ## Touch:FAVOURITES KEY_SEARCH 0x629D ## Touch:SEARCH KEY_HOME 0x22DD ## Touch:HOME KEY_TITLE 0xA25D ## Touch:NOW PLAYING end codes end remote squeezelite-1.9.9-1395+git20220104.874e4f9/tools/lircrc000066400000000000000000000044761416534420200214500ustar00rootroot00000000000000############################ ## KEY_POWER (0x768940bf) ## ############################ begin remote = Slim_Devices_Squeezebox3 button = KEY_POWER repeat = 0 prog = squeezelite config = power end ################################# ## KEY_VOLUMEDOWN (0x768900ff) ## ################################# begin remote = Slim_Devices_Squeezebox3 button = KEY_VOLUMEDOWN repeat = 0 prog = squeezelite config = voldown end ############################### ## KEY_VOLUMEUP (0x7689807f) ## ############################### begin remote = Slim_Devices_Squeezebox3 button = KEY_VOLUMEUP repeat = 0 prog = squeezelite config = volup end ########################### ## KEY_MUTE (0x7689c43b) ## ########################### begin remote = Slim_Devices_Squeezebox3 button = Brightness repeat = 0 prog = squeezelite config = muting end ########################### ## KEY_PLAY (0x768910ef) ## ########################### begin remote = Slim_Devices_Squeezebox3 button = KEY_PLAY repeat = 0 prog = squeezelite config = play end ############################ ## KEY_PAUSE (0x768920df) ## ############################ begin remote = Slim_Devices_Squeezebox3 button = KEY_PAUSE repeat = 0 prog = squeezelite config = pause end ########################### ## KEY_NEXT (0x7689a05f) ## ########################### begin remote = Slim_Devices_Squeezebox3 button = KEY_FORWARD repeat = 0 prog = squeezelite config = fwd end ############################### ## KEY_PREVIOUS (0x7689c03f) ## ############################### begin remote = Slim_Devices_Squeezebox3 button = KEY_REWIND repeat = 0 prog = squeezelite config = rew end ########################### ## power_on (0x76898f70) ## ########################### ############################ ## power_off (0x76898778) ## ############################ ########################### ## preset_1 (0x76898a75) ## ########################### ########################### ## preset_2 (0x76894ab5) ## ########################### ########################### ## preset_3 (0x7689ca35) ## ########################### ########################### ## preset_4 (0x76892ad5) ## ########################### ########################### ## preset_5 (0x7689aa55) ## ########################### ########################### ## preset_6 (0x76896a95) ## ########################### squeezelite-1.9.9-1395+git20220104.874e4f9/tools/setrpath.c000066400000000000000000000137101416534420200222340ustar00rootroot00000000000000/************************************************************************/ /* setrpath */ /* */ /* By Davin Milun (milun@cs.buffalo.edu) */ /* Last modified: Sun Feb 26 12:21:06 EST 1995 */ /* */ /* Program to set the RPATH in an ELF executable. */ /* However, it cannot set the RPATH longer than the RPATH set at */ /* compile time. */ /* */ /* Send any bug reports/fixes/suggestions to milun@cs.buffalo.edu */ /************************************************************************/ /************************************************************************/ /* Copyright (C) 1995, Davin Milun */ /* Permission to use and modify this software for any purpose other */ /* than its incorporation into a commercial product is hereby granted */ /* without fee. */ /* */ /* Permission to copy and distribute this software only for */ /* non-commercial use is also granted without fee, provided, however, */ /* that the above copyright notice appear in all copies, that both that */ /* copyright notice and this permission notice appear in supporting */ /* documentation. The author makes no representations about the */ /* suitability of this software for any purpose. It is provided */ /* ``as is'' without express or implied warranty. */ /* */ /* Compile: gcc -o setrpath -lelf -O setrpath.c */ /************************************************************************/ #include #include #include #include #include #include #include #include #include #define USAGE "USAGE:\t%s [-f] \n\ \t%s -r \n" #define ORNULL(s) (s?s:"(null)") int main(int argc, char *argv[]) { int file; Elf *elf; Elf_Scn *scn, *strscn; Elf32_Shdr *scn_shdr; Elf32_Ehdr *scn_ehdr; Elf_Data *data, *strdata; Elf32_Dyn *dyn; size_t strscnndx; int oldlen, newlen=0, extra_space; char *oldrpath, *newrpath=NULL; unsigned char *strbuffer; int strbuffersize; int forceflag=0, readonly=0; int hasrpath=0; extern char *optarg; extern int optind; int c; while ((c = getopt(argc, argv, "fr")) != EOF){ switch(c){ case 'f': forceflag=1; break; case 'r': readonly=1; break; case '?': fprintf(stderr, USAGE, argv[0], argv[0]); break; } } if (argc != (optind+2-readonly)) { fprintf(stderr,"Wrong number of arguments\n"); fprintf(stderr, USAGE, argv[0], argv[0]); exit(1); } if (!readonly){ newrpath = strdup(argv[optind+1]); newlen = strlen(newrpath); } if (elf_version(EV_CURRENT) == EV_NONE) { fprintf(stderr,"Old version of ELF.\n"); exit(2); } if ((file = open(argv[optind],(readonly?O_RDONLY:O_RDWR))) == -1) { fprintf(stderr,"Cannot open %s for %s\n",(readonly?"reading":"writing"), ORNULL(argv[optind])); perror("open"); exit(3); } elf = elf_begin(file, (readonly?ELF_C_READ:ELF_C_RDWR), (Elf *)NULL); if (elf_kind(elf) != ELF_K_ELF) { fprintf(stderr,"%s is not an ELF file.\n",argv[optind]); exit(4); } if ((scn_ehdr = elf32_getehdr(elf)) == 0) { fprintf(stderr,"elf32_getehdr failed.\n"); exit(5); } scn = NULL; /* Process sections */ while ((scn = elf_nextscn(elf, scn)) != NULL) { scn_shdr = elf32_getshdr(scn); /* Only look at SHT_DYNAMIC section */ if (scn_shdr->sh_type == SHT_DYNAMIC) { data = NULL; /* Process data blocks in the section */ while ((data = elf_getdata(scn, data)) != NULL) { dyn = (Elf32_Dyn *) data->d_buf; /* Process entries in dynamic linking table */ while (dyn->d_tag != DT_NULL) { /* Look at DT_RPATH entry */ if (dyn->d_tag == DT_RPATH) { hasrpath++; scn_shdr = elf32_getshdr(scn); strscnndx = scn_shdr->sh_link; oldrpath = elf_strptr(elf, strscnndx, dyn->d_un.d_ptr); printf("%s RPATH: %s\n",(readonly?"Current":"Old"),ORNULL(oldrpath)); oldlen = strlen(oldrpath); if (readonly) { /* Quit now if readonly*/ elf_end(elf); exit(0); } /* Load the section that contains the strings */ strscn = elf_getscn(elf,strscnndx); strdata = NULL; while ((strdata = elf_getdata(strscn, strdata)) != NULL) { strbuffersize = strdata->d_size; strbuffer = strdata->d_buf; /* Get next data block if needed */ if ((dyn->d_un.d_ptr > (strdata->d_off + strdata->d_size)) || (dyn->d_un.d_ptr < strdata->d_off)) { fprintf(stderr,"The string table is not in one data block\n"); fprintf(stderr,"This is not handled by this program\n"); exit(6); } /* See if there is "slack" after end of RPATH */ extra_space = strdata->d_size - (dyn->d_un.d_ptr + oldlen + 1); /* Mark the data block as dirty */ elf_flagdata(strdata,ELF_C_SET,ELF_F_DIRTY); if (newlen > (oldlen + extra_space)) { fprintf(stderr,"New RPATH would be longer than current RPATH \ plus any extra space.\n"); fprintf(stderr,"Aborting...\n"); exit(7); } if ((newlen > oldlen) && !forceflag) { fprintf(stderr,"New RPATH would be longer than current RPATH.\n"); fprintf(stderr,"(Use -f to use any extra space in string table)\n"); exit(8); } /* Since it will fit in the old place, we can do it */ memmove(&strbuffer[dyn->d_un.d_ptr], newrpath, newlen); strbuffer[dyn->d_un.d_ptr+newlen] = 0; } /* while elf_getdata on the string table */ } /* if (dyn->d_tag == DT_RPATH) */ dyn++; } /* while (dyn->d_tag != DT_NULL) */ } /* while elf_getdata */ } /* if SHT_DYNAMIC */ } /* while elf_nextscn */ if (readonly) { printf("ELF file \"%s\" contains no RPATH.\n",argv[optind]); exit(0); } if (hasrpath) { if (elf_update(elf, ELF_C_WRITE) == -1 ) { fprintf(stderr,"elf_update failed.\n"); exit(9); } printf("New RPATH set to: %s\n", ORNULL(newrpath)); } else { fprintf(stderr,"ELF file \"%s\" contains no RPATH - cannot set one.\n",argv[optind]); exit(10); } elf_end(elf); exit(0); } squeezelite-1.9.9-1395+git20220104.874e4f9/utils.c000066400000000000000000000263021416534420200204030ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "squeezelite.h" #if LINUX || OSX || FREEBSD #include #include #include #if FREEBSD #include #include #include #endif #endif #if SUN #include #include #include #include #include #include #include #include #endif #if WIN #include #if USE_SSL #include #include #include #endif #endif #if OSX #include #include #include #include #endif #include // logging functions const char *logtime(void) { static char buf[100]; #if WIN SYSTEMTIME lt; GetLocalTime(<); sprintf(buf, "[%02d:%02d:%02d.%03d]", lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds); #else struct timeval tv; gettimeofday(&tv, NULL); strftime(buf, sizeof(buf), "[%T.", localtime(&tv.tv_sec)); sprintf(buf+strlen(buf), "%06ld]", (long)tv.tv_usec); #endif return buf; } void logprint(const char *fmt, ...) { va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fflush(stderr); } // cmdline parsing char *next_param(char *src, char c) { static char *str = NULL; char *ptr, *ret; if (src) str = src; if (str && (ptr = strchr(str, c))) { ret = str; *ptr = '\0'; str = ptr + 1; } else { ret = str; str = NULL; } return ret && ret[0] ? ret : NULL; } // clock u32_t gettime_ms(void) { #if WIN return GetTickCount(); #else #if LINUX || FREEBSD struct timespec ts; #ifdef CLOCK_MONOTONIC if (!clock_gettime(CLOCK_MONOTONIC, &ts)) { #else if (!clock_gettime(CLOCK_REALTIME, &ts)) { #endif return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; } #endif struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000 + tv.tv_usec / 1000; #endif } // mac address #if LINUX && !defined(SUN) // search first 4 interfaces returned by IFCONF void get_mac(u8_t mac[]) { char *utmac; struct ifconf ifc; struct ifreq *ifr, *ifend; struct ifreq ifreq; struct ifreq ifs[4]; utmac = getenv("UTMAC"); if (utmac) { if ( strlen(utmac) == 17 ) { if (sscanf(utmac,"%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) == 6) { return; } } } mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0; int s = socket(AF_INET, SOCK_DGRAM, 0); ifc.ifc_len = sizeof(ifs); ifc.ifc_req = ifs; if (ioctl(s, SIOCGIFCONF, &ifc) == 0) { ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq)); for (ifr = ifc.ifc_req; ifr < ifend; ifr++) { if (ifr->ifr_addr.sa_family == AF_INET) { strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name) - 1); if (ioctl (s, SIOCGIFHWADDR, &ifreq) == 0) { memcpy(mac, ifreq.ifr_hwaddr.sa_data, 6); if (mac[0]+mac[1]+mac[2] != 0) { break; } } } } } close(s); } #endif #if SUN void get_mac(u8_t mac[]) { struct arpreq parpreq; struct sockaddr_in *psa; struct in_addr inaddr; struct hostent *phost; char hostname[MAXHOSTNAMELEN]; char **paddrs; char *utmac; int sock; int status=0; utmac = getenv("UTMAC"); if (utmac) { if ( strlen(utmac) == 17 ) { if (sscanf(utmac,"%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]) == 6) { return; } } } mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0; gethostname(hostname, MAXHOSTNAMELEN); phost = gethostbyname(hostname); paddrs = phost->h_addr_list; memcpy(&inaddr.s_addr, *paddrs, sizeof(inaddr.s_addr)); sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if(sock == -1) { mac[5] = 1; return; } memset(&parpreq, 0, sizeof(struct arpreq)); psa = (struct sockaddr_in *) &parpreq.arp_pa; memset(psa, 0, sizeof(struct sockaddr_in)); psa->sin_family = AF_INET; memcpy(&psa->sin_addr, *paddrs, sizeof(struct in_addr)); status = ioctl(sock, SIOCGARP, &parpreq); if(status == -1) { mac[5] = 2; return; } mac[0] = (unsigned char) parpreq.arp_ha.sa_data[0]; mac[1] = (unsigned char) parpreq.arp_ha.sa_data[1]; mac[2] = (unsigned char) parpreq.arp_ha.sa_data[2]; mac[3] = (unsigned char) parpreq.arp_ha.sa_data[3]; mac[4] = (unsigned char) parpreq.arp_ha.sa_data[4]; mac[5] = (unsigned char) parpreq.arp_ha.sa_data[5]; } #endif #if OSX || FREEBSD void get_mac(u8_t mac[]) { struct ifaddrs *addrs, *ptr; const struct sockaddr_dl *dlAddr; const unsigned char *base; mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0; if (getifaddrs(&addrs) == 0) { ptr = addrs; while (ptr) { if (ptr->ifa_addr->sa_family == AF_LINK && ((const struct sockaddr_dl *) ptr->ifa_addr)->sdl_type == IFT_ETHER) { dlAddr = (const struct sockaddr_dl *)ptr->ifa_addr; base = (const unsigned char*) &dlAddr->sdl_data[dlAddr->sdl_nlen]; memcpy(mac, base, min(dlAddr->sdl_alen, 6)); break; } ptr = ptr->ifa_next; } freeifaddrs(addrs); } } #endif #if WIN #pragma comment(lib, "IPHLPAPI.lib") void get_mac(u8_t mac[]) { IP_ADAPTER_INFO AdapterInfo[16]; DWORD dwBufLen = sizeof(AdapterInfo); DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen); mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0; if (GetAdaptersInfo(AdapterInfo, &dwBufLen) == ERROR_SUCCESS) { memcpy(mac, AdapterInfo[0].Address, 6); } } #endif void set_nonblock(sockfd s) { #if WIN u_long iMode = 1; ioctlsocket(s, FIONBIO, &iMode); #else int flags = fcntl(s, F_GETFL,0); fcntl(s, F_SETFL, flags | O_NONBLOCK); #endif } // connect for socket already set to non blocking with timeout in seconds int connect_timeout(sockfd sock, const struct sockaddr *addr, socklen_t addrlen, int timeout) { fd_set w, e; struct timeval tval; if (connect(sock, addr, addrlen) < 0) { #if !WIN if (last_error() != EINPROGRESS) { #else if (last_error() != WSAEWOULDBLOCK) { #endif return -1; } } FD_ZERO(&w); FD_SET(sock, &w); e = w; tval.tv_sec = timeout; tval.tv_usec = 0; // only return 0 if w set and sock error is zero, otherwise return error code if (select(sock + 1, NULL, &w, &e, timeout ? &tval : NULL) == 1 && FD_ISSET(sock, &w)) { int error = 0; socklen_t len = sizeof(error); getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&error, &len); return error; } return -1; } void server_addr(char *server, in_addr_t *ip_ptr, unsigned *port_ptr) { struct addrinfo *res = NULL; struct addrinfo hints; const char *port = NULL; if (strtok(server, ":")) { port = strtok(NULL, ":"); if (port) { *port_ptr = atoi(port); } } memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; getaddrinfo(server, NULL, &hints, &res); if (res && res->ai_addr) { *ip_ptr = ((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr; } if (res) { freeaddrinfo(res); } } void set_readwake_handles(event_handle handles[], sockfd s, event_event e) { #if WINEVENT handles[0] = WSACreateEvent(); handles[1] = e; WSAEventSelect(s, handles[0], FD_READ | FD_CLOSE); #elif SELFPIPE handles[0].fd = s; handles[1].fd = e.fds[0]; handles[0].events = POLLIN; handles[1].events = POLLIN; #else handles[0].fd = s; handles[1].fd = e; handles[0].events = POLLIN; handles[1].events = POLLIN; #endif } event_type wait_readwake(event_handle handles[], int timeout) { #if WINEVENT int wait = WSAWaitForMultipleEvents(2, handles, FALSE, timeout, FALSE); if (wait == WSA_WAIT_EVENT_0) { WSAResetEvent(handles[0]); return EVENT_READ; } else if (wait == WSA_WAIT_EVENT_0 + 1) { return EVENT_WAKE; } else { return EVENT_TIMEOUT; } #else if (poll(handles, 2, timeout) > 0) { if (handles[0].revents) { return EVENT_READ; } if (handles[1].revents) { wake_clear(handles[1].fd); return EVENT_WAKE; } } return EVENT_TIMEOUT; #endif } // pack/unpack to network byte order void packN(u32_t *dest, u32_t val) { u8_t *ptr = (u8_t *)dest; *(ptr) = (val >> 24) & 0xFF; *(ptr+1) = (val >> 16) & 0xFF; *(ptr+2) = (val >> 8) & 0xFF; *(ptr+3) = val & 0xFF; } void packn(u16_t *dest, u16_t val) { u8_t *ptr = (u8_t *)dest; *(ptr) = (val >> 8) & 0xFF; *(ptr+1) = val & 0xFF; } u32_t unpackN(u32_t *src) { u8_t *ptr = (u8_t *)src; return *(ptr) << 24 | *(ptr+1) << 16 | *(ptr+2) << 8 | *(ptr+3); } u16_t unpackn(u16_t *src) { u8_t *ptr = (u8_t *)src; return *(ptr) << 8 | *(ptr+1); } #if OSX void set_nosigpipe(sockfd s) { int set = 1; setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); } #endif #if WIN void winsock_init(void) { WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); int WSerr = WSAStartup(wVersionRequested, &wsaData); if (WSerr != 0) { LOG_ERROR("Bad winsock version"); exit(1); } } void winsock_close(void) { WSACleanup(); } void *dlopen(const char *filename, int flag) { SetLastError(0); return LoadLibrary((LPCTSTR)filename); } void *dlsym(void *handle, const char *symbol) { SetLastError(0); return (void *)GetProcAddress(handle, symbol); } char *dlerror(void) { static char ret[32]; int last = GetLastError(); if (last) { sprintf(ret, "code: %i", last); SetLastError(0); return ret; } return NULL; } // this only implements numfds == 1 int poll(struct pollfd *fds, unsigned long numfds, int timeout) { fd_set r, w; struct timeval tv; int ret; FD_ZERO(&r); FD_ZERO(&w); if (fds[0].events & POLLIN) FD_SET(fds[0].fd, &r); if (fds[0].events & POLLOUT) FD_SET(fds[0].fd, &w); tv.tv_sec = timeout / 1000; tv.tv_usec = 1000 * (timeout % 1000); ret = select(fds[0].fd + 1, &r, &w, NULL, &tv); if (ret < 0) return ret; fds[0].revents = 0; if (FD_ISSET(fds[0].fd, &r)) fds[0].revents |= POLLIN; if (FD_ISSET(fds[0].fd, &w)) fds[0].revents |= POLLOUT; return ret; } #endif #if LINUX || FREEBSD void touch_memory(u8_t *buf, size_t size) { u8_t *ptr; for (ptr = buf; ptr < buf + size; ptr += sysconf(_SC_PAGESIZE)) { *ptr = 0; } } #endif #if WIN char *strcasestr(const char *haystack, const char *needle) { size_t length_needle; size_t length_haystack; size_t i; if (!haystack || !needle) return NULL; length_needle = strlen(needle); length_haystack = strlen(haystack) - length_needle + 1; for (i = 0; i < length_haystack; i++) { size_t j; for (j = 0; j < length_needle; j++) { unsigned char c1; unsigned char c2; c1 = haystack[i+j]; c2 = needle[j]; if (toupper(c1) != toupper(c2)) goto next; } return (char *) haystack + i; next: ; } return NULL; } #endif squeezelite-1.9.9-1395+git20220104.874e4f9/vorbis.c000066400000000000000000000237621416534420200205560ustar00rootroot00000000000000/* * Squeezelite - lightweight headless squeezebox emulator * * (c) Adrian Smith 2012-2015, triode1@btinternet.com * Ralph Irving 2015-2021, ralph_irving@hotmail.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #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 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 #ifdef TREMOR_ONLY #include #else #include #endif struct vorbis { OggVorbis_File *vf; bool opened; #if FRAME_BUF u8_t *write_buf; #endif #if !LINKALL // 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); #endif }; static struct vorbis *v; extern log_level loglevel; extern struct buffer *streambuf; extern struct buffer *outputbuf; extern struct streamstate stream; extern struct outputstate output; extern struct decodestate decode; extern struct processstate process; #define LOCK_S mutex_lock(streambuf->mutex) #define UNLOCK_S mutex_unlock(streambuf->mutex) #define LOCK_O mutex_lock(outputbuf->mutex) #define UNLOCK_O mutex_unlock(outputbuf->mutex) #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 #else #define OV(h, fn, ...) (h)->ov_##fn(__VA_ARGS__) #define TREMOR(h) (h)->ov_read_tremor #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; LOCK_S; bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); bytes = min(bytes, size * nmemb); memcpy(ptr, streambuf->readp, bytes); _buf_inc_readp(streambuf, bytes); UNLOCK_S; return bytes / size; } // 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; LOCK_S; if (stream.state <= DISCONNECT && !_buf_used(streambuf)) { UNLOCK_S; return DECODE_COMPLETE; } UNLOCK_S; if (decode.new_stream) { ov_callbacks cbs; int err; struct vorbis_info *info; 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 ((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); 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); return DECODE_ERROR; } } #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 // 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 (n > 0) { frames_t count; s16_t *iptr; ISAMPLE_T *optr; 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 #else while (count--) { *--optr = ALIGN(*--iptr); } #endif } else if (channels == 1) { while (count--) { *--optr = ALIGN(*--iptr); *--optr = ALIGN(*iptr); } } IF_DIRECT( _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); ); IF_PROCESS( process.in_frames = frames; ); LOG_SDEBUG("wrote %u frames", frames); } else if (n == 0) { if (stream.state <= DISCONNECT) { LOG_INFO("partial 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); UNLOCK_O_direct; return DECODE_COMPLETE; } UNLOCK_O_direct; return DECODE_RUNNING; } 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; } } } static void vorbis_close(void) { if (v->opened) { OV(v, clear, v->vf); v->opened = false; } free(v->vf); #if FRAME_BUF free(v->write_buf); v->write_buf = NULL; #endif v->vf = NULL; } 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) { tremor = true; } else { LOG_INFO("dlerror: %s", dlerror()); return false; } } 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"); if ((err = dlerror()) != NULL) { LOG_INFO("dlerror: %s", err); return false; } LOG_INFO("loaded %s", tremor ? LIBTREMOR : LIBVORBIS); #endif return true; } struct codec *register_vorbis(void) { static struct codec ret = { 'o', // id "ogg", // types 4096, // min read 20480, // min space vorbis_open, // open vorbis_close, // close vorbis_decode,// decode }; v = malloc(sizeof(struct vorbis)); if (!v) { return NULL; } v->vf = NULL; v->opened = false; if (!load_vorbis()) { return NULL; } LOG_INFO("using vorbis to decode ogg"); return &ret; }