New upstream version 1.9+git20201216.e02fa87
tony mancill
2 years ago
0 | Version 1.0 - 15/2/13 | |
1 | ===================== | |
2 | - initial release | |
3 | ||
4 | Version 1.1 - 12/4/13 | |
5 | ===================== | |
6 | ||
7 | Minor changes | |
8 | - add timeout on slimproto connection to detect dead server | |
9 | - fix issue with clipping on windows by disabling portaudio dither | |
10 | - silence alsa error messages on linux alsa builds unless debugging is enabled | |
11 | - hide some additional error messages unless debuging is enabled so usb dacs produce less error messages when turned off and on | |
12 | ||
13 | Version 1.2 - 6/7/13 | |
14 | ==================== | |
15 | ||
16 | Features | |
17 | - support of upsampling via libsoxr | |
18 | ||
19 | Minor changes | |
20 | - command line option for setting the service address now requires "-s" before the server address | |
21 | - fixes a bug where the channels could become swapped when using S16_LE ALSA output | |
22 | - falls back to polling for a new server if one is not found for more than 30 seconds | |
23 | - fixes play of wav/aiff local files when the LocalPlayer plugin is active | |
24 | ||
25 | Version 1.3 - 6/10/13 | |
26 | ===================== | |
27 | ||
28 | Features | |
29 | - support for wma/alac decode via ffmpeg library (requires compilation with -DFFMPEG) | |
30 | - support for export of audio data to jivelite to enable visulizations on linux (requires compilation with -DVISEXPORT) | |
31 | ||
32 | Minor changes | |
33 | - support async as well as sync resampling rates | |
34 | - support on/off of audio device with portaudio | |
35 | - improved gapless support for aac/mad when skipping to mid track (based on patches from Wouter Ellenbroek) | |
36 | - various bug fixes | |
37 | ||
38 | Version 1.3.1 - 25/11/13 | |
39 | ======================== | |
40 | ||
41 | Minor changes | |
42 | - support of compile time linking for distro packaging, uses -DLINKALL option | |
43 | ||
44 | Version 1.4 28/12/13 | |
45 | ==================== | |
46 | ||
47 | Features | |
48 | - native support of dsd playback to dop capable dac or via conversion to pcm and resampling | |
49 | - support dop in flac playback to dop dacs | |
50 | - support of output to stdout | |
51 | ||
52 | Minor changes | |
53 | - support of resampling only when sample rate is not natively supported | |
54 | - fix problem with libmpg123 playback not playing to end of track | |
55 | - add ablity for player name change to be stored locally in a file (to emulate hardware where name is stored on player) | |
56 | ||
57 | Version 1.5 12/1/14 | |
58 | =================== | |
59 | ||
60 | Minor changes | |
61 | - add configurable delay for switch between pcm and dop | |
62 | - allow visexport to work with jivelite running as any user | |
63 | - bug fixes for dsf playback, for status progress on windows using wdm-ks output, and to avoid 100% cpu | |
64 | - change some logging levels for slimproto to aid readability | |
65 | ||
66 | Version 1.6 23/3/14 | |
67 | =================== | |
68 | ||
69 | Minor changes | |
70 | - add support for direct file playback on windows | |
71 | - add configurable delay for switch between pcm sample rates | |
72 | - support build on freebsd | |
73 | - fix gapless playback on portaudio builds | |
74 | - fix gapless playback for mp3 localfile case with tags at start of file | |
75 | ||
76 | Version 1.6.1 22/4/14 | |
77 | ===================== | |
78 | ||
79 | Minor changes | |
80 | - fix bug with PA version changing sample rate between tracks | |
81 | - fix crash when skipping in ogg while resampling | |
82 | - fix typo | |
83 | ||
84 | Version 1.6.2 26/5/14 | |
85 | ===================== | |
86 | ||
87 | Minor changes | |
88 | - fix XRUN on track change when resampling on low power cpus | |
89 | - log command line to logfile when debugging enabled | |
90 | - option to exclude codecs (-e) | |
91 | - support parallel execution of libsoxr | |
92 | ||
93 | Version 1.6.3 14/6/14 | |
94 | ===================== | |
95 | ||
96 | Minor changes | |
97 | - reduce time to start track when playing local files | |
98 | - disable use of OPENMP when RESAMPLE build option defined, add new option RESAMPLE_MP to enable it | |
99 | ||
100 | Version 1.6.4 7/7/14 | |
101 | ==================== | |
102 | ||
103 | Minor changes | |
104 | - improve synchronisation feedback accuracy | |
105 | ||
106 | Version 1.6.5 21/11/14 | |
107 | ====================== | |
108 | ||
109 | Minor changes | |
110 | - fix problem opening ALSA device if 44100 is not supported | |
111 | - trap setting of hw player mac address | |
112 | ||
113 | Version 1.7 1/1/15 | |
114 | ================== | |
115 | ||
116 | Minor changes | |
117 | - allow player modelname to be set at compile or run time | |
118 | - workaround alsa drivers reporting very large number of available frames | |
119 | - fix clicks on localfile playback of AIFF files | |
120 | - add -P option to store process id in a file | |
121 | - improve error messages for command line parsing | |
122 | ||
123 | Version 1.7.1 10/1/15 | |
124 | ===================== | |
125 | ||
126 | Minor changes | |
127 | - fix crash which could occur when resampling | |
128 | ||
129 | Version 1.8 1/2/15 | |
130 | ================== | |
131 | ||
132 | Features | |
133 | - support for closing output device when idle with -C option | |
134 | - support for basic IR input using LIRC on Linux | |
135 | - support for volume adjustment or unmuting of alsa mixer | |
136 | - support for inverting output polarity via LMS setting (requires recent 7.9 server) |
0 | 0 | Squeezelite - lightweight headless squeezebox emulator |
1 | 1 | |
2 | 2 | (c) Adrian Smith 2012-2015, triode1@btinternet.com |
3 | Ralph Irving 2015-2017, ralph_irving@hotmail.com | |
3 | 4 | |
4 | 5 | Released under GPLv3 license: |
5 | 6 |
0 | # Cross compile support - create a Makefile which defines these three variables and then includes this Makefile... | |
1 | CFLAGS ?= -Wall -fPIC -O2 $(OPTS) | |
2 | LDFLAGS ?= -lasound -lpthread -lm -lrt | |
0 | #Cross compile support - create a Makefile which defines these three variables and then includes this Makefile... | |
1 | CFLAGS ?= -Wall -fPIC -O2 | |
2 | CFLAGS += -fcommon | |
3 | LDADD ?= -lpthread -lm -lrt | |
3 | 4 | EXECUTABLE ?= squeezelite |
4 | 5 | |
5 | 6 | # passing one or more of these in $(OPTS) enables optional feature inclusion |
6 | 7 | OPT_DSD = -DDSD |
7 | 8 | OPT_FF = -DFFMPEG |
9 | OPT_ALAC = -DALAC | |
8 | 10 | OPT_LINKALL = -DLINKALL |
9 | 11 | OPT_RESAMPLE= -DRESAMPLE |
10 | 12 | OPT_VIS = -DVISEXPORT |
11 | 13 | OPT_IR = -DIR |
14 | OPT_GPIO = -DGPIO | |
15 | OPT_RPI = -DRPI | |
16 | OPT_NO_FAAD = -DNO_FAAD | |
17 | OPT_SSL = -DUSE_SSL | |
18 | OPT_NOSSLSYM= -DNO_SSLSYM | |
19 | OPT_OPUS = -DOPUS | |
20 | OPT_PORTAUDIO = -DPORTAUDIO | |
21 | OPT_PULSEAUDIO = -DPULSEAUDIO | |
12 | 22 | |
13 | 23 | SOURCES = \ |
14 | 24 | main.c slimproto.c buffer.c stream.c utils.c \ |
15 | output.c output_alsa.c output_pa.c output_stdout.c output_pack.c decode.c \ | |
16 | flac.c pcm.c mad.c vorbis.c faad.c mpg.c | |
25 | output.c output_alsa.c output_pa.c output_stdout.c output_pack.c output_pulse.c decode.c \ | |
26 | flac.c pcm.c mad.c vorbis.c mpg.c | |
17 | 27 | |
18 | 28 | SOURCES_DSD = dsd.c dop.c dsd2pcm/dsd2pcm.c |
19 | 29 | SOURCES_FF = ffmpeg.c |
30 | SOURCES_ALAC = alac.c alac_wrapper.cpp | |
20 | 31 | SOURCES_RESAMPLE = process.c resample.c |
21 | 32 | SOURCES_VIS = output_vis.c |
22 | 33 | SOURCES_IR = ir.c |
34 | SOURCES_GPIO = gpio.c | |
35 | SOURCES_RPI = minimal_gpio.c | |
36 | SOURCES_FAAD = faad.c | |
37 | SOURCES_SSL = sslsym.c | |
38 | SOURCES_OPUS = opus.c | |
23 | 39 | |
24 | 40 | LINK_LINUX = -ldl |
41 | LINK_ALSA = -lasound | |
42 | LINK_PORTAUDIO = -lportaudio | |
43 | LINK_PULSEAUDIO = -lpulse | |
44 | LINK_SSL = -lssl -lcrypto | |
45 | LINK_ALAC = -lalac | |
25 | 46 | |
26 | LINKALL = -lFLAC -lmad -lvorbisfile -lfaad -lmpg123 | |
27 | LINKALL_FF = -lavcodec -lavformat -lavutil | |
47 | LINKALL = -lmad -lmpg123 -lFLAC -lvorbisfile -lvorbis -logg | |
48 | LINKALL_FF = -lavformat -lavcodec -lavutil | |
28 | 49 | LINKALL_RESAMPLE = -lsoxr |
29 | 50 | LINKALL_IR = -llirc_client |
51 | LINKALL_FAAD = -lfaad | |
52 | LINKALL_OPUS = -lopusfile -lopus | |
30 | 53 | |
31 | 54 | DEPS = squeezelite.h slimproto.h |
32 | 55 | |
33 | 56 | UNAME = $(shell uname -s) |
34 | 57 | |
35 | 58 | # add optional sources |
36 | ifneq (,$(findstring $(OPT_DSD), $(CFLAGS))) | |
59 | ifneq (,$(findstring $(OPT_DSD), $(OPTS))) | |
37 | 60 | SOURCES += $(SOURCES_DSD) |
38 | 61 | endif |
39 | ifneq (,$(findstring $(OPT_FF), $(CFLAGS))) | |
62 | ifneq (,$(findstring $(OPT_FF), $(OPTS))) | |
40 | 63 | SOURCES += $(SOURCES_FF) |
41 | 64 | endif |
42 | ifneq (,$(findstring $(OPT_RESAMPLE), $(CFLAGS))) | |
65 | ifneq (,$(findstring $(OPT_ALAC), $(OPTS))) | |
66 | SOURCES += $(SOURCES_ALAC) | |
67 | DEPS += alac_wrapper.h | |
68 | endif | |
69 | ifneq (,$(findstring $(OPT_OPUS), $(OPTS))) | |
70 | SOURCES += $(SOURCES_OPUS) | |
71 | endif | |
72 | ifneq (,$(findstring $(OPT_RESAMPLE), $(OPTS))) | |
43 | 73 | SOURCES += $(SOURCES_RESAMPLE) |
44 | 74 | endif |
45 | ifneq (,$(findstring $(OPT_VIS), $(CFLAGS))) | |
75 | ifneq (,$(findstring $(OPT_VIS), $(OPTS))) | |
46 | 76 | SOURCES += $(SOURCES_VIS) |
47 | 77 | endif |
48 | ifneq (,$(findstring $(OPT_IR), $(CFLAGS))) | |
78 | ifneq (,$(findstring $(OPT_IR), $(OPTS))) | |
49 | 79 | SOURCES += $(SOURCES_IR) |
80 | endif | |
81 | ifneq (,$(findstring $(OPT_GPIO), $(OPTS))) | |
82 | SOURCES += $(SOURCES_GPIO) | |
83 | endif | |
84 | ifneq (,$(findstring $(OPT_RPI), $(OPTS))) | |
85 | SOURCES += $(SOURCES_RPI) | |
86 | endif | |
87 | # ensure GPIO is enabled with RPI | |
88 | ifneq (,$(findstring $(OPT_RPI), $(OPTS))) | |
89 | ifeq (,$(findstring $(SOURCES_GPIO), $(SOURCES))) | |
90 | CFLAGS += $(OPT_GPIO) | |
91 | SOURCES += $(SOURCES_GPIO) | |
92 | endif | |
93 | endif | |
94 | ifeq (,$(findstring $(OPT_NO_FAAD), $(OPTS))) | |
95 | SOURCES += $(SOURCES_FAAD) | |
96 | endif | |
97 | ifneq (,$(findstring $(OPT_SSL), $(OPTS))) | |
98 | SOURCES += $(SOURCES_SSL) | |
50 | 99 | endif |
51 | 100 | |
52 | 101 | # add optional link options |
53 | ifneq (,$(findstring $(OPT_LINKALL), $(CFLAGS))) | |
54 | LDFLAGS += $(LINKALL) | |
55 | ifneq (,$(findstring $(OPT_FF), $(CFLAGS))) | |
56 | LDFLAGS += $(LINKALL_FF) | |
102 | ifneq (,$(findstring $(OPT_LINKALL), $(OPTS))) | |
103 | LDADD += $(LINKALL) | |
104 | ifneq (,$(findstring $(OPT_FF), $(OPTS))) | |
105 | LDADD += $(LINKALL_FF) | |
57 | 106 | endif |
58 | ifneq (,$(findstring $(OPT_RESAMPLE), $(CFLAGS))) | |
59 | LDFLAGS += $(LINKALL_RESAMPLE) | |
107 | ifneq (,$(findstring $(OPT_OPUS), $(OPTS))) | |
108 | LDADD += $(LINKALL_OPUS) | |
60 | 109 | endif |
61 | ifneq (,$(findstring $(OPT_IR), $(CFLAGS))) | |
62 | LDFLAGS += $(LINKALL_IR) | |
110 | ifneq (,$(findstring $(OPT_RESAMPLE), $(OPTS))) | |
111 | LDADD += $(LINKALL_RESAMPLE) | |
112 | endif | |
113 | ifneq (,$(findstring $(OPT_IR), $(OPTS))) | |
114 | LDADD += $(LINKALL_IR) | |
115 | endif | |
116 | ifeq (,$(findstring $(OPT_NO_FAAD), $(OPTS))) | |
117 | LDADD += $(LINKALL_FAAD) | |
118 | endif | |
119 | ifneq (,$(findstring $(OPT_SSL), $(OPTS))) | |
120 | LDADD += $(LINK_SSL) | |
63 | 121 | endif |
64 | 122 | else |
65 | 123 | # if not LINKALL and linux add LINK_LINUX |
66 | 124 | ifeq ($(UNAME), Linux) |
67 | LDFLAGS += $(LINK_LINUX) | |
125 | LDADD += $(LINK_LINUX) | |
126 | endif | |
127 | ifneq (,$(findstring $(OPT_NOSSLSYM), $(OPTS))) | |
128 | LDADD += $(LINK_SSL) | |
68 | 129 | endif |
69 | 130 | endif |
70 | 131 | |
71 | OBJECTS = $(SOURCES:.c=.o) | |
132 | ifneq (,$(findstring $(OPT_PULSEAUDIO), $(OPTS))) | |
133 | LDADD += $(LINK_PULSEAUDIO) | |
134 | else ifneq (,$(findstring $(OPT_PORTAUDIO), $(OPTS))) | |
135 | LDADD += $(LINK_PORTAUDIO) | |
136 | else | |
137 | LDADD += $(LINK_ALSA) | |
138 | endif | |
139 | ||
140 | ifneq (,$(findstring $(OPT_ALAC), $(OPTS))) | |
141 | LDADD += $(LINK_ALAC) | |
142 | endif | |
143 | ||
144 | OBJECTS = $(addsuffix .o,$(basename $(SOURCES))) | |
72 | 145 | |
73 | 146 | all: $(EXECUTABLE) |
74 | 147 | |
75 | 148 | $(EXECUTABLE): $(OBJECTS) |
76 | $(CC) $(OBJECTS) $(LDFLAGS) -o $@ | |
149 | ifneq (,$(findstring $(OPT_ALAC), $(OPTS))) | |
150 | $(CXX) $(OBJECTS) $(LDFLAGS) $(LDADD) -o $@ | |
151 | else | |
152 | $(CC) $(OBJECTS) $(LDFLAGS) $(LDADD) -o $@ | |
153 | endif | |
77 | 154 | |
78 | 155 | $(OBJECTS): $(DEPS) |
79 | 156 | |
157 | .cpp.o: | |
158 | $(CXX) $(CXXFLAGS) $(CFLAGS) $(CPPFLAGS) $(OPTS) -Wno-multichar $< -c -o $@ | |
159 | ||
80 | 160 | .c.o: |
81 | $(CC) $(CFLAGS) $(CPPFLAGS) $< -c -o $@ | |
161 | $(CC) $(CFLAGS) $(CPPFLAGS) $(OPTS) $< -c -o $@ | |
82 | 162 | |
83 | 163 | clean: |
84 | 164 | rm -f $(OBJECTS) $(EXECUTABLE) |
165 | ||
166 | print-%: | |
167 | @echo $* = $($*) |
0 | # Makefile armel | |
1 | OPTS = -DRESAMPLE -DFFMPEG -DVISEXPORT -DDSD -DIR -DGPIO -I/usr/local/include | |
2 | CFLAGS ?= -s -Wall -fPIC -O3 -march=armv5te $(OPTS) | |
3 | LDFLAGS ?= -s -lasound -lpthread -ldl -lrt -Wl,-rpath,/usr/local/lib | |
4 | EXECUTABLE ?= squeezelite | |
5 | ||
6 | 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 | |
7 | ||
8 | DEPS = squeezelite.h slimproto.h dsd2pcm/dsd2pcm.h | |
9 | ||
10 | OBJECTS = $(SOURCES:.c=.o) | |
11 | ||
12 | all: $(EXECUTABLE) | |
13 | ||
14 | $(EXECUTABLE): $(OBJECTS) | |
15 | $(CC) $(OBJECTS) $(LDFLAGS) -o $@ | |
16 | ||
17 | $(OBJECTS): $(DEPS) | |
18 | ||
19 | .c.o: | |
20 | $(CC) $(CFLAGS) $< -c -o $@ | |
21 | ||
22 | clean: | |
23 | rm -f $(OBJECTS) $(EXECUTABLE) |
0 | OPTS = -DFFMPEG -DRESAMPLE -DGPIO -DUSE_SSL | |
0 | 1 | CPPFLAGS = -I/usr/local/include -I/usr/local/include/portaudio2 |
1 | LDFLAGS = -L/usr/local/lib -L/usr/local/lib/portaudio2 -lportaudio -lpthread -lm | |
2 | LDFLAGS = -L/usr/local/lib /usr/local/lib/libportaudio.a -lm | |
2 | 3 | |
3 | 4 | include Makefile |
0 | # OSX 10.5 Intel 32-bit | |
1 | OPTS = -DPORTAUDIO -DALAC -DOPUS -DRESAMPLE -DGPIO -DLINKALL -DVISEXPORT -DDSD -DUSE_SSL -I./include -I./include/opus -I./include/alac -O2 -I./include -isysroot /Developer/SDKs/MacOSX10.5.sdk -arch i386 | |
2 | ||
3 | LDFLAGS = -Wl,-syslibroot,/Developer/SDKs/MacOSX10.5.sdk -arch i386 -mmacosx-version-min=10.5 -L./lib | |
4 | ||
5 | LDADD = -lportaudio -lpthread -ldl -lm -framework CoreVideo -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon | |
6 | ||
7 | EXECUTABLE = squeezelite-i386 | |
8 | ||
9 | include Makefile |
0 | # Cross compile support - create a Makefile which defines these three variables and then includes this Makefile... | |
1 | CFLAGS ?= -Wall -DPORTAUDIO -DPA18API -fPIC -O3 -I`pwd`/include $(OPTS) | |
2 | LDFLAGS ?= -lpthread -lm -ldl -lrt -L`pwd`/lib -lportaudio | |
3 | EXECUTABLE ?= squeezelite-oss | |
4 | ||
5 | 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 | |
6 | DEPS = squeezelite.h slimproto.h | |
7 | ||
8 | OBJECTS = $(SOURCES:.c=.o) | |
9 | ||
10 | all: $(EXECUTABLE) | |
11 | ||
12 | $(EXECUTABLE): $(OBJECTS) | |
13 | $(CC) $(OBJECTS) $(LDFLAGS) -o $@ | |
14 | ||
15 | $(OBJECTS): $(DEPS) | |
16 | ||
17 | .c.o: | |
18 | $(CC) $(CFLAGS) $< -c -o $@ | |
19 | ||
20 | clean: | |
21 | rm -f $(OBJECTS) $(EXECUTABLE) |
0 | # OSX 10.4 PPC 32-bit | |
1 | CC=powerpc-apple-darwin10-gcc | |
2 | CXX=powerpc-apple-darwin10-gcc | |
3 | CFLAGS ?= -s -Wall -fPIC -DOSXPPC -DLINKALL -O2 -I./include -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mcpu=G3 -arch ppc | |
4 | 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 | |
5 | EXECUTABLE ?= squeezelite-ppc | |
6 | ||
7 | 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 | |
8 | ||
9 | DEPS = squeezelite.h slimproto.h | |
10 | ||
11 | OBJECTS = $(SOURCES:.c=.o) | |
12 | ||
13 | all: $(EXECUTABLE) | |
14 | ||
15 | $(EXECUTABLE): $(OBJECTS) | |
16 | $(CC) $(OBJECTS) $(LDFLAGS) -o $@ | |
17 | ||
18 | $(OBJECTS): $(DEPS) | |
19 | ||
20 | .c.o: | |
21 | $(CC) $(CFLAGS) $< -c -o $@ | |
22 | ||
23 | clean: | |
24 | rm -f $(OBJECTS) $(EXECUTABLE) |
0 | # OSX 10.3 PPC 64-bit | |
1 | CC=powerpc-apple-darwin10-gcc | |
2 | CXX=powerpc-apple-darwin10-gcc | |
3 | CFLAGS ?= -s -Wall -fPIC -DOSXPPC -DLINKALL -O2 -I./include64 -isysroot /Developer/SDKs/MacOSX10.5.sdk -m64 -arch ppc64 -mmacosx-version-min=10.3 | |
4 | 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 | |
5 | EXECUTABLE ?= squeezelite-ppc64 | |
6 | ||
7 | 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 | |
8 | ||
9 | DEPS = squeezelite.h slimproto.h | |
10 | ||
11 | OBJECTS = $(SOURCES:.c=.o) | |
12 | ||
13 | all: $(EXECUTABLE) | |
14 | ||
15 | $(EXECUTABLE): $(OBJECTS) | |
16 | $(CC) $(OBJECTS) $(LDFLAGS) -o $@ | |
17 | ||
18 | $(OBJECTS): $(DEPS) | |
19 | ||
20 | .c.o: | |
21 | $(CC) $(CFLAGS) $< -c -o $@ | |
22 | ||
23 | clean: | |
24 | rm -f $(OBJECTS) $(EXECUTABLE) |
0 | # Make with PulseAudio rather than direct alsa | |
1 | OPTS += -DPULSEAUDIO | |
2 | LDADD = -lrt -lpulse -lpthread -lm | |
3 | EXECUTABLE = squeezelite-pulse | |
4 | ||
5 | include Makefile |
0 | OPTS = -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 | |
1 | ||
2 | LDFLAGS=-L./lib -L/usr/local/lib -s -lgomp | |
3 | ||
4 | include Makefile |
0 | # Solaris SPARC portaudio v18 api | |
1 | CC = gcc | |
2 | CPP = cpp | |
3 | CFLAGS ?= -fPIC -Wall -O3 -DRESAMPLE -DGPIO -I`pwd`/include -s | |
4 | LDFLAGS ?= -lpthread -lsocket -lnsl -ldl -lrt -lm -L`pwd`/lib -lportaudio -R/opt/squeezelite/lib -s | |
5 | EXECUTABLE ?= squeezelite-sun | |
6 | ||
7 | 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 | |
8 | DEPS = squeezelite.h slimproto.h dsd2pcm/dsd2pcm.h | |
9 | ||
10 | OBJECTS = $(SOURCES:.c=.o) | |
11 | ||
12 | all: $(EXECUTABLE) | |
13 | ||
14 | $(EXECUTABLE): $(OBJECTS) | |
15 | $(CC) $(OBJECTS) $(LDFLAGS) -o $@ | |
16 | ||
17 | $(OBJECTS): $(DEPS) | |
18 | ||
19 | .c.o: | |
20 | $(CC) $(CFLAGS) $< -c -o $@ | |
21 | ||
22 | clean: | |
23 | rm -f $(OBJECTS) $(EXECUTABLE) |
0 | # OSX 10.7+ 64-bit only | |
1 | OPTS = -DPORTAUDIO -DALAC -DOPUS -DRESAMPLE -DGPIO -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 | |
2 | ||
3 | 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 | |
4 | ||
5 | LDADD = -lportaudio -lpthread -ldl -lm -framework CoreVideo -framework VideoDecodeAcceleration -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon | |
6 | ||
7 | include Makefile |
0 | Squeezelite v1.9.x, Copyright 2012-2015 Adrian Smith, 2015-2019 Ralph Irving.<br> | |
1 | <br> | |
2 | See the squeezelite manpage for usage details.<br> | |
3 | https://ralph-irving.github.io/squeezelite.html<br> | |
4 | <br> | |
5 | This program is free software: you can redistribute it and/or modify<br> | |
6 | it under the terms of the GNU General Public License as published by<br> | |
7 | the Free Software Foundation, either version 3 of the License, or<br> | |
8 | (at your option) any later version.<br> | |
9 | <br> | |
10 | This program is distributed in the hope that it will be useful,<br> | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of<br> | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br> | |
13 | GNU General Public License for more details.<br> | |
14 | <br> | |
15 | You should have received a copy of the GNU General Public License<br> | |
16 | along with this program. If not, see <http://www.gnu.org/licenses/>.<br> | |
17 | <br> | |
18 | Contains dsd2pcm library Copyright 2009, 2011 Sebastian Gesemann which<br> | |
19 | is subject to its own license.<br> | |
20 | <br> | |
21 | Contains the Daphile Project full dsd patch Copyright 2013-2017 Daphile,<br> | |
22 | which is subject to its own license.<br> | |
23 | <br> | |
24 | Option to allow server side upsampling for PCM streams (-W) from<br> | |
25 | squeezelite-R2 (c) Marco Curti 2015, marcoc1712@gmail.com.<br> | |
26 | <br> | |
27 | RaspberryPi minimal GPIO Interface<br> | |
28 | http://abyz.me.uk/rpi/pigpio/examples.html#Misc_minimal_gpio.<br> | |
29 | <br> | |
30 | This software uses libraries from the FFmpeg project under<br> | |
31 | the LGPLv2.1 and its source can be downloaded from<br> | |
32 | https://sourceforge.net/projects/lmsclients/files/source/<br> |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.com | |
4 | * (c) Philippe, philippe_44@outlook.com | |
5 | * | |
6 | * This program is free software: you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation, either version 3 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | * | |
19 | */ | |
20 | ||
21 | #include "squeezelite.h" | |
22 | ||
23 | #if ALAC | |
24 | #include "alac_wrapper.h" | |
25 | ||
26 | #if BYTES_PER_FRAME == 4 | |
27 | #define ALIGN8(n) (n << 8) | |
28 | #define ALIGN16(n) (n) | |
29 | #define ALIGN24(n) (n >> 8) | |
30 | #define ALIGN32(n) (n >> 16) | |
31 | #else | |
32 | #define ALIGN8(n) (n << 24) | |
33 | #define ALIGN16(n) (n << 16) | |
34 | #define ALIGN24(n) (n << 8) | |
35 | #define ALIGN32(n) (n) | |
36 | #endif | |
37 | ||
38 | #define BLOCK_SIZE (4096 * BYTES_PER_FRAME) | |
39 | #define MIN_READ BLOCK_SIZE | |
40 | #define MIN_SPACE (MIN_READ * 4) | |
41 | ||
42 | struct chunk_table { | |
43 | u32_t sample, offset; | |
44 | }; | |
45 | ||
46 | struct alac { | |
47 | void *decoder; | |
48 | u8_t *writebuf; | |
49 | // following used for mp4 only | |
50 | u32_t consume; | |
51 | u32_t pos; | |
52 | u32_t sample; | |
53 | u32_t nextchunk; | |
54 | void *stsc; | |
55 | u32_t skip; | |
56 | u64_t samples; | |
57 | u64_t sttssamples; | |
58 | bool empty; | |
59 | struct chunk_table *chunkinfo; | |
60 | u32_t *block_size, default_block_size, block_index; | |
61 | unsigned sample_rate; | |
62 | unsigned char channels, sample_size; | |
63 | unsigned trak, play; | |
64 | }; | |
65 | ||
66 | static struct alac *l; | |
67 | ||
68 | extern log_level loglevel; | |
69 | ||
70 | extern struct buffer *streambuf; | |
71 | extern struct buffer *outputbuf; | |
72 | extern struct streamstate stream; | |
73 | extern struct outputstate output; | |
74 | extern struct decodestate decode; | |
75 | extern struct processstate process; | |
76 | ||
77 | #define LOCK_S mutex_lock(streambuf->mutex) | |
78 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
79 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
80 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
81 | #if PROCESS | |
82 | #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex) | |
83 | #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex) | |
84 | #define LOCK_O_not_direct if (!decode.direct) mutex_lock(outputbuf->mutex) | |
85 | #define UNLOCK_O_not_direct if (!decode.direct) mutex_unlock(outputbuf->mutex) | |
86 | #define IF_DIRECT(x) if (decode.direct) { x } | |
87 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
88 | #else | |
89 | #define LOCK_O_direct mutex_lock(outputbuf->mutex) | |
90 | #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex) | |
91 | #define LOCK_O_not_direct | |
92 | #define UNLOCK_O_not_direct | |
93 | #define IF_DIRECT(x) { x } | |
94 | #define IF_PROCESS(x) | |
95 | #endif | |
96 | ||
97 | // read mp4 header to extract config data | |
98 | static int read_mp4_header(void) { | |
99 | size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
100 | char type[5]; | |
101 | u32_t len; | |
102 | ||
103 | while (bytes >= 8) { | |
104 | // count trak to find the first playable one | |
105 | u32_t consume; | |
106 | ||
107 | len = unpackN((u32_t *)streambuf->readp); | |
108 | memcpy(type, streambuf->readp + 4, 4); | |
109 | type[4] = '\0'; | |
110 | ||
111 | if (!strcmp(type, "moov")) { | |
112 | l->trak = 0; | |
113 | l->play = 0; | |
114 | } | |
115 | if (!strcmp(type, "trak")) { | |
116 | l->trak++; | |
117 | } | |
118 | ||
119 | // extract audio config from within alac | |
120 | if (!strcmp(type, "alac") && bytes > len) { | |
121 | u8_t *ptr = streambuf->readp + 36; | |
122 | l->decoder = alac_create_decoder(len - 36, ptr, &l->sample_size, &l->sample_rate, &l->channels); | |
123 | l->play = l->trak; | |
124 | } | |
125 | ||
126 | // extract the total number of samples from stts | |
127 | if (!strcmp(type, "stsz") && bytes > len) { | |
128 | u32_t i; | |
129 | u8_t *ptr = streambuf->readp + 12; | |
130 | l->default_block_size = unpackN((u32_t *) ptr); ptr += 4; | |
131 | if (!l->default_block_size) { | |
132 | u32_t entries = unpackN((u32_t *)ptr); ptr += 4; | |
133 | l->block_size = malloc((entries + 1)* 4); | |
134 | for (i = 0; i < entries; i++) { | |
135 | l->block_size[i] = unpackN((u32_t *)ptr); ptr += 4; | |
136 | } | |
137 | l->block_size[entries] = 0; | |
138 | LOG_DEBUG("total blocksize contained in stsz %u", entries); | |
139 | } else { | |
140 | LOG_DEBUG("fixed blocksize in stsz %u", l->default_block_size); | |
141 | } | |
142 | } | |
143 | ||
144 | // extract the total number of samples from stts | |
145 | if (!strcmp(type, "stts") && bytes > len) { | |
146 | u32_t i; | |
147 | u8_t *ptr = streambuf->readp + 12; | |
148 | u32_t entries = unpackN((u32_t *)ptr); | |
149 | ptr += 4; | |
150 | for (i = 0; i < entries; ++i) { | |
151 | u32_t count = unpackN((u32_t *)ptr); | |
152 | u32_t size = unpackN((u32_t *)(ptr + 4)); | |
153 | l->sttssamples += count * size; | |
154 | ptr += 8; | |
155 | } | |
156 | LOG_DEBUG("total number of samples contained in stts: " FMT_u64, l->sttssamples); | |
157 | } | |
158 | ||
159 | // stash sample to chunk info, assume it comes before stco | |
160 | if (!strcmp(type, "stsc") && bytes > len && !l->chunkinfo) { | |
161 | l->stsc = malloc(len - 12); | |
162 | if (l->stsc == NULL) { | |
163 | LOG_WARN("malloc fail"); | |
164 | return -1; | |
165 | } | |
166 | memcpy(l->stsc, streambuf->readp + 12, len - 12); | |
167 | } | |
168 | ||
169 | // build offsets table from stco and stored stsc | |
170 | if (!strcmp(type, "stco") && bytes > len && l->play == l->trak) { | |
171 | u32_t i; | |
172 | // extract chunk offsets | |
173 | u8_t *ptr = streambuf->readp + 12; | |
174 | u32_t entries = unpackN((u32_t *)ptr); | |
175 | ptr += 4; | |
176 | l->chunkinfo = malloc(sizeof(struct chunk_table) * (entries + 1)); | |
177 | if (l->chunkinfo == NULL) { | |
178 | LOG_WARN("malloc fail"); | |
179 | return -1; | |
180 | } | |
181 | for (i = 0; i < entries; ++i) { | |
182 | l->chunkinfo[i].offset = unpackN((u32_t *)ptr); | |
183 | l->chunkinfo[i].sample = 0; | |
184 | ptr += 4; | |
185 | } | |
186 | l->chunkinfo[i].sample = 0; | |
187 | l->chunkinfo[i].offset = 0; | |
188 | // fill in first sample id for each chunk from stored stsc | |
189 | if (l->stsc) { | |
190 | u32_t stsc_entries = unpackN((u32_t *)l->stsc); | |
191 | u32_t sample = 0; | |
192 | u32_t last = 0, last_samples = 0; | |
193 | u8_t *ptr = (u8_t *)l->stsc + 4; | |
194 | while (stsc_entries--) { | |
195 | u32_t first = unpackN((u32_t *)ptr); | |
196 | u32_t samples = unpackN((u32_t *)(ptr + 4)); | |
197 | if (last) { | |
198 | for (i = last - 1; i < first - 1; ++i) { | |
199 | l->chunkinfo[i].sample = sample; | |
200 | sample += last_samples; | |
201 | } | |
202 | } | |
203 | if (stsc_entries == 0) { | |
204 | for (i = first - 1; i < entries; ++i) { | |
205 | l->chunkinfo[i].sample = sample; | |
206 | sample += samples; | |
207 | } | |
208 | } | |
209 | last = first; | |
210 | last_samples = samples; | |
211 | ptr += 12; | |
212 | } | |
213 | free(l->stsc); | |
214 | l->stsc = NULL; | |
215 | } | |
216 | } | |
217 | ||
218 | // found media data, advance to start of first chunk and return | |
219 | if (!strcmp(type, "mdat")) { | |
220 | _buf_inc_readp(streambuf, 8); | |
221 | l->pos += 8; | |
222 | bytes -= 8; | |
223 | if (l->play) { | |
224 | LOG_DEBUG("type: mdat len: %u pos: %u", len, l->pos); | |
225 | if (l->chunkinfo && l->chunkinfo[0].offset > l->pos) { | |
226 | u32_t skip = l->chunkinfo[0].offset - l->pos; | |
227 | LOG_DEBUG("skipping: %u", skip); | |
228 | if (skip <= bytes) { | |
229 | _buf_inc_readp(streambuf, skip); | |
230 | l->pos += skip; | |
231 | } else { | |
232 | l->consume = skip; | |
233 | } | |
234 | } | |
235 | l->sample = l->nextchunk = 1; | |
236 | l->block_index = 0; | |
237 | return 1; | |
238 | } else { | |
239 | LOG_DEBUG("type: mdat len: %u, no playable track found", len); | |
240 | return -1; | |
241 | } | |
242 | } | |
243 | ||
244 | // parse key-value atoms within ilst ---- entries to get encoder padding within iTunSMPB entry for gapless | |
245 | if (!strcmp(type, "----") && bytes > len) { | |
246 | u8_t *ptr = streambuf->readp + 8; | |
247 | u32_t remain = len - 8, size; | |
248 | if (!memcmp(ptr + 4, "mean", 4) && (size = unpackN((u32_t *)ptr)) < remain) { | |
249 | ptr += size; remain -= size; | |
250 | } | |
251 | if (!memcmp(ptr + 4, "name", 4) && (size = unpackN((u32_t *)ptr)) < remain && !memcmp(ptr + 12, "iTunSMPB", 8)) { | |
252 | ptr += size; remain -= size; | |
253 | } | |
254 | if (!memcmp(ptr + 4, "data", 4) && remain > 16 + 48) { | |
255 | // data is stored as hex strings: 0 start end samples | |
256 | u32_t b, c; u64_t d; | |
257 | if (sscanf((const char *)(ptr + 16), "%x %x %x " FMT_x64, &b, &b, &c, &d) == 4) { | |
258 | LOG_DEBUG("iTunSMPB start: %u end: %u samples: " FMT_u64, b, c, d); | |
259 | if (l->sttssamples && l->sttssamples < b + c + d) { | |
260 | LOG_DEBUG("reducing samples as stts count is less"); | |
261 | d = l->sttssamples - (b + c); | |
262 | } | |
263 | l->skip = b; | |
264 | l->samples = d; | |
265 | } | |
266 | } | |
267 | } | |
268 | ||
269 | // default to consuming entire box | |
270 | consume = len; | |
271 | ||
272 | // read into these boxes so reduce consume | |
273 | if (!strcmp(type, "moov") || !strcmp(type, "trak") || !strcmp(type, "mdia") || !strcmp(type, "minf") || !strcmp(type, "stbl") || | |
274 | !strcmp(type, "udta") || !strcmp(type, "ilst")) { | |
275 | consume = 8; | |
276 | } | |
277 | // special cases which mix mix data in the enclosing box which we want to read into | |
278 | if (!strcmp(type, "stsd")) consume = 16; | |
279 | if (!strcmp(type, "mp4a")) consume = 36; | |
280 | if (!strcmp(type, "meta")) consume = 12; | |
281 | ||
282 | // consume rest of box if it has been parsed (all in the buffer) or is not one we want to parse | |
283 | if (bytes >= consume) { | |
284 | LOG_DEBUG("type: %s len: %u consume: %u", type, len, consume); | |
285 | _buf_inc_readp(streambuf, consume); | |
286 | l->pos += consume; | |
287 | bytes -= consume; | |
288 | } else if ( !(!strcmp(type, "esds") || !strcmp(type, "stts") || !strcmp(type, "stsc") || | |
289 | !strcmp(type, "stsz") || !strcmp(type, "stco") || !strcmp(type, "----")) ) { | |
290 | LOG_DEBUG("type: %s len: %u consume: %u - partial consume: %u", type, len, consume, bytes); | |
291 | _buf_inc_readp(streambuf, bytes); | |
292 | l->pos += bytes; | |
293 | l->consume = consume - bytes; | |
294 | break; | |
295 | } else if (len > streambuf->size) { | |
296 | // can't process an atom larger than streambuf! | |
297 | LOG_ERROR("atom %s too large for buffer %u %u", type, len, streambuf->size); | |
298 | return -1; | |
299 | } else { | |
300 | // make sure there is 'len' contiguous space | |
301 | _buf_unwrap(streambuf, len); | |
302 | break; | |
303 | } | |
304 | } | |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
309 | static decode_state alac_decode(void) { | |
310 | size_t bytes; | |
311 | bool endstream; | |
312 | u8_t *iptr; | |
313 | u32_t frames, block_size; | |
314 | ||
315 | LOCK_S; | |
316 | ||
317 | // data not reached yet | |
318 | if (l->consume) { | |
319 | u32_t consume = min(l->consume, _buf_used(streambuf)); | |
320 | LOG_DEBUG("consume: %u of %u", consume, l->consume); | |
321 | _buf_inc_readp(streambuf, consume); | |
322 | l->pos += consume; | |
323 | l->consume -= consume; | |
324 | UNLOCK_S; | |
325 | return DECODE_RUNNING; | |
326 | } | |
327 | ||
328 | if (decode.new_stream) { | |
329 | int found = 0; | |
330 | ||
331 | // mp4 - read header | |
332 | found = read_mp4_header(); | |
333 | ||
334 | if (found == 1) { | |
335 | bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
336 | ||
337 | LOG_INFO("setting track_start"); | |
338 | LOCK_O; | |
339 | ||
340 | output.next_sample_rate = decode_newstream(l->sample_rate, output.supported_rates); | |
341 | IF_DSD( output.next_fmt = PCM; ) | |
342 | output.track_start = outputbuf->writep; | |
343 | if (output.fade_mode) _checkfade(true); | |
344 | decode.new_stream = false; | |
345 | ||
346 | UNLOCK_O; | |
347 | } else if (found == -1) { | |
348 | LOG_WARN("[%p]: error reading stream header"); | |
349 | UNLOCK_S; | |
350 | return DECODE_ERROR; | |
351 | } else { | |
352 | // not finished header parsing come back next time | |
353 | UNLOCK_S; | |
354 | return DECODE_RUNNING; | |
355 | } | |
356 | } | |
357 | ||
358 | bytes = _buf_used(streambuf); | |
359 | block_size = l->default_block_size ? l->default_block_size : l->block_size[l->block_index]; | |
360 | ||
361 | // stream terminated | |
362 | if (stream.state <= DISCONNECT && (bytes == 0 || block_size == 0)) { | |
363 | UNLOCK_S; | |
364 | LOG_DEBUG("end of stream"); | |
365 | return DECODE_COMPLETE; | |
366 | } | |
367 | ||
368 | // is there enough data for decoding | |
369 | if (bytes < block_size) { | |
370 | UNLOCK_S; | |
371 | return DECODE_RUNNING; | |
372 | } else if (block_size != l->default_block_size) l->block_index++; | |
373 | ||
374 | bytes = min(bytes, _buf_cont_read(streambuf)); | |
375 | ||
376 | // need to create a buffer with contiguous data | |
377 | if (bytes < block_size) { | |
378 | u8_t *buffer = malloc(block_size); | |
379 | memcpy(buffer, streambuf->readp, bytes); | |
380 | memcpy(buffer + bytes, streambuf->buf, block_size - bytes); | |
381 | iptr = buffer; | |
382 | } else iptr = streambuf->readp; | |
383 | ||
384 | if (!alac_to_pcm(l->decoder, iptr, l->writebuf, 2, &frames)) { | |
385 | LOG_ERROR("decode error"); | |
386 | UNLOCK_S; | |
387 | return DECODE_ERROR; | |
388 | } | |
389 | ||
390 | // and free it | |
391 | if (bytes < block_size) free(iptr); | |
392 | ||
393 | LOG_SDEBUG("block of %u bytes (%u frames)", block_size, frames); | |
394 | ||
395 | endstream = false; | |
396 | // mp4 end of chunk - skip to next offset | |
397 | if (l->chunkinfo && l->chunkinfo[l->nextchunk].offset && l->sample++ == l->chunkinfo[l->nextchunk].sample) { | |
398 | if (l->chunkinfo[l->nextchunk].offset > l->pos) { | |
399 | u32_t skip = l->chunkinfo[l->nextchunk].offset - l->pos; | |
400 | if (_buf_used(streambuf) >= skip) { | |
401 | _buf_inc_readp(streambuf, skip); | |
402 | l->pos += skip; | |
403 | } else { | |
404 | l->consume = skip; | |
405 | } | |
406 | l->nextchunk++; | |
407 | } else { | |
408 | LOG_ERROR("error: need to skip backwards!"); | |
409 | endstream = true; | |
410 | } | |
411 | // mp4 when not at end of chunk | |
412 | } else if (frames) { | |
413 | _buf_inc_readp(streambuf, block_size); | |
414 | l->pos += block_size; | |
415 | } else { | |
416 | endstream = true; | |
417 | } | |
418 | ||
419 | UNLOCK_S; | |
420 | ||
421 | if (endstream) { | |
422 | LOG_WARN("unable to decode further"); | |
423 | return DECODE_ERROR; | |
424 | } | |
425 | ||
426 | // now point at the beginning of decoded samples | |
427 | iptr = l->writebuf; | |
428 | ||
429 | if (l->skip) { | |
430 | u32_t skip; | |
431 | if (l->empty) { | |
432 | l->empty = false; | |
433 | l->skip -= frames; | |
434 | LOG_DEBUG("gapless: first frame empty, skipped %u frames at start", frames); | |
435 | } | |
436 | skip = min(frames, l->skip); | |
437 | LOG_DEBUG("gapless: skipping %u frames at start", skip); | |
438 | frames -= skip; | |
439 | l->skip -= skip; | |
440 | iptr += skip * l->channels * l->sample_size; | |
441 | } | |
442 | ||
443 | if (l->samples) { | |
444 | if (l->samples < frames) { | |
445 | LOG_DEBUG("gapless: trimming %u frames from end", frames - l->samples); | |
446 | frames = (u32_t) l->samples; | |
447 | } | |
448 | l->samples -= frames; | |
449 | } | |
450 | ||
451 | LOCK_O_direct; | |
452 | ||
453 | while (frames > 0) { | |
454 | size_t f, count; | |
455 | ISAMPLE_T *optr; | |
456 | ||
457 | IF_DIRECT( | |
458 | f = min(frames, _buf_cont_write(outputbuf) / BYTES_PER_FRAME); | |
459 | optr = (ISAMPLE_T *)outputbuf->writep; | |
460 | ); | |
461 | IF_PROCESS( | |
462 | f = min(frames, process.max_in_frames - process.in_frames); | |
463 | optr = (ISAMPLE_T *)((u8_t *) process.inbuf + process.in_frames * BYTES_PER_FRAME); | |
464 | ); | |
465 | ||
466 | f = min(f, frames); | |
467 | count = f; | |
468 | ||
469 | if (l->sample_size == 8) { | |
470 | while (count--) { | |
471 | *optr++ = ALIGN8(*iptr++); | |
472 | *optr++ = ALIGN8(*iptr++); | |
473 | } | |
474 | } else if (l->sample_size == 16) { | |
475 | u16_t *_iptr = (u16_t*) iptr; | |
476 | while (count--) { | |
477 | *optr++ = ALIGN16(*_iptr++); | |
478 | *optr++ = ALIGN16(*_iptr++); | |
479 | } | |
480 | } else if (l->sample_size == 24) { | |
481 | while (count--) { | |
482 | *optr++ = ALIGN24(*(u32_t*) iptr); | |
483 | *optr++ = ALIGN24(*(u32_t*) (iptr + 3)); | |
484 | iptr += 6; | |
485 | } | |
486 | } else if (l->sample_size == 32) { | |
487 | u32_t *_iptr = (u32_t*) iptr; | |
488 | while (count--) { | |
489 | *optr++ = ALIGN32(*_iptr++); | |
490 | *optr++ = ALIGN32(*_iptr++); | |
491 | } | |
492 | } else { | |
493 | LOG_ERROR("unsupported bits per sample: %u", l->sample_size); | |
494 | } | |
495 | ||
496 | frames -= f; | |
497 | ||
498 | IF_DIRECT( | |
499 | _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); | |
500 | ); | |
501 | IF_PROCESS( | |
502 | process.in_frames = f; | |
503 | // called only if there is enough space in process buffer | |
504 | if (frames) LOG_ERROR("unhandled case"); | |
505 | ); | |
506 | } | |
507 | ||
508 | UNLOCK_O_direct; | |
509 | ||
510 | return DECODE_RUNNING; | |
511 | } | |
512 | ||
513 | static void alac_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
514 | if (l->decoder) alac_delete_decoder(l->decoder); | |
515 | else l->writebuf = malloc(BLOCK_SIZE * 2); | |
516 | ||
517 | if (l->chunkinfo) free(l->chunkinfo); | |
518 | if (l->block_size) free(l->block_size); | |
519 | if (l->stsc) free(l->stsc); | |
520 | l->decoder = l->chunkinfo = l->stsc = l->block_size = NULL; | |
521 | l->skip = 0; | |
522 | l->samples = l->sttssamples = 0; | |
523 | l->empty = false; | |
524 | l->pos = l->consume = l->sample = l->nextchunk = 0; | |
525 | } | |
526 | ||
527 | static void alac_close(void) { | |
528 | if (l->decoder) alac_delete_decoder(l->decoder); | |
529 | if (l->chunkinfo) free(l->chunkinfo); | |
530 | if (l->block_size) free(l->block_size); | |
531 | if (l->stsc) free(l->stsc); | |
532 | l->decoder = l->chunkinfo = l->stsc = l->block_size = NULL; | |
533 | free(l->writebuf); | |
534 | } | |
535 | ||
536 | struct codec *register_alac(void) { | |
537 | static struct codec ret = { | |
538 | 'l', // id | |
539 | "alc", // types | |
540 | MIN_READ, // min read | |
541 | MIN_SPACE, // min space assuming a ratio of 2 | |
542 | alac_open, // open | |
543 | alac_close, // close | |
544 | alac_decode, // decode | |
545 | }; | |
546 | ||
547 | l = malloc(sizeof(struct alac)); | |
548 | if (!l) { | |
549 | return NULL; | |
550 | } | |
551 | ||
552 | l->decoder = l->chunkinfo = l->stsc = l->block_size = NULL; | |
553 | ||
554 | LOG_INFO("using alac to decode alc"); | |
555 | return &ret; | |
556 | } | |
557 | ||
558 | #endif /* ALAC */ |
0 | /* | |
1 | * alac_wrapper.c - ALAC decoder wrapper | |
2 | * | |
3 | * (c) Philippe, philippe_44@outlook.com | |
4 | * | |
5 | * This program is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | * | |
18 | */ | |
19 | ||
20 | #include <stdlib.h> | |
21 | ||
22 | #include "ALACDecoder.h" | |
23 | #include "ALACBitUtilities.h" | |
24 | #include "alac_wrapper.h" | |
25 | ||
26 | typedef struct alac_codec_s { | |
27 | ALACDecoder *Decoder; | |
28 | unsigned block_size, frames_per_packet; | |
29 | } alac_codec_t; | |
30 | ||
31 | /*----------------------------------------------------------------------------*/ | |
32 | extern "C" struct alac_codec_s *alac_create_decoder(int magic_cookie_size, unsigned char *magic_cookie, | |
33 | unsigned char *sample_size, unsigned *sample_rate, | |
34 | unsigned char *channels) { | |
35 | struct alac_codec_s *codec = (struct alac_codec_s*) malloc(sizeof(struct alac_codec_s)); | |
36 | ||
37 | codec->Decoder = new ALACDecoder; | |
38 | codec->Decoder->Init(magic_cookie, magic_cookie_size); | |
39 | ||
40 | *channels = codec->Decoder->mConfig.numChannels; | |
41 | *sample_rate = codec->Decoder->mConfig.sampleRate; | |
42 | *sample_size = codec->Decoder->mConfig.bitDepth; | |
43 | ||
44 | codec->frames_per_packet = codec->Decoder->mConfig.frameLength; | |
45 | codec->block_size = codec->frames_per_packet * (*channels) * (*sample_size) / 8; | |
46 | ||
47 | return codec; | |
48 | } | |
49 | ||
50 | /*----------------------------------------------------------------------------*/ | |
51 | extern "C" void alac_delete_decoder(struct alac_codec_s *codec) { | |
52 | delete (ALACDecoder*) codec->Decoder; | |
53 | free(codec); | |
54 | } | |
55 | ||
56 | /*----------------------------------------------------------------------------*/ | |
57 | extern "C" bool alac_to_pcm(struct alac_codec_s *codec, unsigned char* input, | |
58 | unsigned char *output, char channels, unsigned *out_frames) { | |
59 | BitBuffer input_buffer; | |
60 | ||
61 | BitBufferInit(&input_buffer, input, codec->block_size); | |
62 | return codec->Decoder->Decode(&input_buffer, output, codec->frames_per_packet, channels, out_frames) == ALAC_noErr; | |
63 | } | |
64 |
0 | /***************************************************************************** | |
1 | * alac_wrapper.h: ALAC coder wrapper | |
2 | * | |
3 | * Copyright (C) 2016 Philippe <philippe44@outlook.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation; either version 2 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. | |
18 | *****************************************************************************/ | |
19 | #ifndef __ALAC_WRAPPER_H_ | |
20 | #define __ALAC_WRAPPER_H_ | |
21 | ||
22 | struct alac_codec_s; | |
23 | ||
24 | #ifdef __cplusplus | |
25 | extern "C" { | |
26 | #endif | |
27 | ||
28 | struct alac_codec_s *alac_create_decoder(int magic_cookie_size, unsigned char *magic_cookie, | |
29 | unsigned char *sample_size, unsigned *sample_rate, | |
30 | unsigned char *channels); | |
31 | void alac_delete_decoder(struct alac_codec_s *codec); | |
32 | bool alac_to_pcm(struct alac_codec_s *codec, unsigned char* input, | |
33 | unsigned char *output, char channels, unsigned *out_frames); | |
34 | ||
35 | #ifdef __cplusplus | |
36 | } | |
37 | #endif | |
38 | ||
39 | #endif⏎ |
0 | # Contributor: Carl Chave <online@chave.us> | |
1 | # Maintainer: Ralph Irving <ralph.irving@gmail.com> | |
2 | pkgname=squeezelite | |
3 | pkgver=1.9.7.1273 | |
4 | pkgrel=1 | |
5 | pkgdesc="Lightweight headless squeezebox player for Logitech Media Server" | |
6 | url="https://github.com/ralph-irving/squeezelite" | |
7 | arch="all" | |
8 | license="GPL-3.0-or-later3" | |
9 | options="!check" # No test suite | |
10 | depends="flac alsa-lib faad2 mpg123 libvorbis libmad soxr openssl opusfile libalac" | |
11 | makedepends="flac-dev alsa-lib-dev faad2-dev mpg123-dev libvorbis-dev libmad-dev soxr-dev openssl-dev opusfile-dev opus-dev libalac-dev" | |
12 | install="$pkgname.pre-install" | |
13 | subpackages="$pkgname-doc $pkgname-openrc" | |
14 | source="$pkgname-$pkgver.zip::https://github.com/ralph-irving/squeezelite/archive/master.zip load-libtremor-first.patch $pkgname.confd $pkgname.initd" | |
15 | builddir="$srcdir/$pkgname-master" | |
16 | ||
17 | build() { | |
18 | cd "$builddir" | |
19 | make OPTS="-DRESAMPLE -DDSD -DGPIO -DVISEXPORT -DUSE_SSL -DNO_SSLSYM -DOPUS -DALAC -I/usr/include/opus -I/usr/include/alac" | |
20 | gcc -Os -fomit-frame-pointer -fcommon -s -o find_servers tools/find_servers.c | |
21 | gcc -Os -fomit-frame-pointer -fcommon -s -o alsacap tools/alsacap.c -lasound | |
22 | } | |
23 | ||
24 | package() { | |
25 | cd "$builddir" | |
26 | install -Dm 755 squeezelite \ | |
27 | "$pkgdir"/usr/bin/squeezelite | |
28 | install -Dm 755 alsacap \ | |
29 | "$pkgdir"/usr/bin/alsacap | |
30 | install -Dm 755 find_servers \ | |
31 | "$pkgdir"/usr/bin/find_servers | |
32 | install -Dm 644 doc/squeezelite.1 \ | |
33 | "$pkgdir"/usr/share/man/man1/squeezelite.1 | |
34 | install -Dm 644 "$srcdir"/squeezelite.confd \ | |
35 | "$pkgdir"/etc/conf.d/squeezelite | |
36 | install -Dm 755 "$srcdir"/squeezelite.initd \ | |
37 | "$pkgdir"/etc/init.d/squeezelite | |
38 | } | |
39 | ||
40 | sha512sums="93d7695abb0bf670590cef73c2d9d484771e63acca39864b60cd819bf4b42fff87f9a82f7d419acce469c493d6bedfaba1207e646dde151bc76af959874f16cd load-libtremor-first.patch | |
41 | ff552fcbbbf2b2291958fa1c61057f54ba0d9b19620666036dd1e19897b5d7bcc543c40699c3ee53b2eec7a38b9cf46cb9205c2048f7d5488c23085d9904f114 squeezelite.confd | |
42 | fdc0358c10c56772d5e70178eac88f62729c22965c530ad989ebcc8aa4af8c871085bdeb8b4ccd96d8cf2e87388258180054d908719f81f2745d97654f6d60eb squeezelite.initd" |
0 | # Maintainer: Ralph Irving <ralph.irving@gmail.com> | |
1 | pkgname=libalac | |
2 | pkgver=1.0.0 | |
3 | pkgrel=1 | |
4 | pkgdesc="Apple Lossless Audio Codec (ALAC) library" | |
5 | url="https://github.com/TimothyGu/alac" | |
6 | arch="all" | |
7 | subpackages="$pkgname-doc $pkgname-dev" | |
8 | license="Apache 2.0" | |
9 | makedepends="git autoconf automake make tar" | |
10 | source="fix-arm-segfault.patch \ | |
11 | alac-version.patch" | |
12 | ||
13 | prepare() { | |
14 | cd "$builddir/../" | |
15 | ||
16 | git clone $url $pkgname-$pkgver | |
17 | ||
18 | cd $pkgname-$pkgver | |
19 | ||
20 | for patchfile in $source; do | |
21 | patch -p1 -i ../$patchfile | |
22 | done | |
23 | } | |
24 | ||
25 | build() { | |
26 | cd "$builddir" | |
27 | autoreconf -if | |
28 | ./configure \ | |
29 | --build=$CBUILD \ | |
30 | --host=$CHOST \ | |
31 | --prefix=/usr | |
32 | ||
33 | make -j1 | |
34 | } | |
35 | ||
36 | check() { | |
37 | cd "$builddir" | |
38 | make check | |
39 | } | |
40 | ||
41 | package() { | |
42 | cd "$builddir" | |
43 | make DESTDIR="$pkgdir" install | |
44 | install -Dm644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/COPYING | |
45 | } | |
46 | ||
47 | sha512sums="e72b13714476170108844b84c0043dc06d2ff2e8c9b651a7ad1571d148fc5567aca48048646d16fb82630d6972a31b9328f04e522972b297d1cf8e804785867f fix-arm-segfault.patch | |
48 | 093379f79b5dc9f5b8aa45826d61738b088d78305a7d514df33851ae34d02ee9034a8ecddf2558fcb1bf4daaf64c620ea4411521908cfc748e31fd0a2d50bbf7 alac-version.patch" |
0 | --- libalac-1.0.0/configure.ac.orig 2020-09-06 10:55:00.450562777 -0400 | |
1 | +++ libalac-1.0.0/configure.ac 2020-09-06 10:56:39.308930134 -0400 | |
2 | @@ -14,7 +14,7 @@ | |
3 | dnl See the License for the specific language governing permissions and | |
4 | dnl limitations under the License. | |
5 | ||
6 | -AC_INIT([alac], [0.0r4+tg1], [nobody]) | |
7 | +AC_INIT([alac], [1.0.0], [nobody]) | |
8 | AC_CONFIG_AUX_DIR(.) | |
9 | AC_CONFIG_MACRO_DIR([m4]) | |
10 | AM_INIT_AUTOMAKE([tar-ustar foreign]) |
0 | diff --git a/codec/EndianPortable.c b/codec/EndianPortable.c | |
1 | index 5a7d5b8..b8423c9 100644 | |
2 | --- a/codec/EndianPortable.c | |
3 | +++ b/codec/EndianPortable.c | |
4 | @@ -40,6 +40,8 @@ | |
5 | #define TARGET_RT_LITTLE_ENDIAN 1 | |
6 | #elif defined (TARGET_OS_WIN32) | |
7 | #define TARGET_RT_LITTLE_ENDIAN 1 | |
8 | +#elif defined (__arm__) || defined(__aarch64__) | |
9 | +#define TARGET_RT_LITTLE_ENDIAN 1 | |
10 | #endif | |
11 | ||
12 | uint16_t Swap16NtoB(uint16_t inUInt16) |
0 | # Maintainer: Ralph Irving <ralph.irving@gmail.com> | |
1 | pkgname=libtremor | |
2 | pkgver=0.19681 | |
3 | pkgrel=0 | |
4 | pkgdesc="Fixed Point Ogg Vorbis compliant software decoder library" | |
5 | url="https://svn.xiph.org/trunk/Tremor" | |
6 | arch="all" | |
7 | license="GPL-3.0-only" | |
8 | subpackages="$pkgname-doc $pkgname-dev" | |
9 | makedepends="libogg-dev subversion autoconf automake" | |
10 | depends="libogg" | |
11 | options="!check" | |
12 | ||
13 | build() { | |
14 | cd "$builddir/../" | |
15 | ||
16 | svn checkout https://svn.xiph.org/trunk/Tremor $pkgname-$pkgver | |
17 | ||
18 | cd $pkgname-$pkgver | |
19 | ||
20 | # Patch for error: | |
21 | # ./configure: line 9215: syntax error near unexpected token `,' | |
22 | # ./configure: line 9215: ` XIPH_PATH_OGG(, as_fn_error $? "must have Ogg installed!" "$LINENO" 5)' | |
23 | sed -i "s/\(XIPH_PATH_OGG(, AC_MSG_ERROR(must have Ogg installed!))\)/#\1/" configure.in | |
24 | ||
25 | ./autogen.sh \ | |
26 | --build=$CBUILD \ | |
27 | --host=$CHOST \ | |
28 | --prefix=/usr \ | |
29 | --enable-static=no | |
30 | ||
31 | make | |
32 | } | |
33 | ||
34 | package() { | |
35 | cd "$builddir" | |
36 | ||
37 | make DESTDIR="$pkgdir" install | |
38 | install -Dm644 COPYING "$pkgdir"/usr/share/licenses/$pkgname/COPYING | |
39 | } |
0 | Index: vorbis.c | |
1 | =================================================================== | |
2 | --- squeezelite/vorbis.c (revision 1213) | |
3 | +++ squeezelite/vorbis.c (working copy) | |
4 | @@ -320,14 +320,14 @@ | |
5 | ||
6 | static bool load_vorbis() { | |
7 | #if !LINKALL | |
8 | - void *handle = dlopen(LIBVORBIS, RTLD_NOW); | |
9 | + void *handle = dlopen(LIBTREMOR, RTLD_NOW); | |
10 | char *err; | |
11 | - bool tremor = false; | |
12 | + bool tremor = true; | |
13 | ||
14 | if (!handle) { | |
15 | - handle = dlopen(LIBTREMOR, RTLD_NOW); | |
16 | + handle = dlopen(LIBVORBIS, RTLD_NOW); | |
17 | if (handle) { | |
18 | - tremor = true; | |
19 | + tremor = false; | |
20 | } else { | |
21 | LOG_INFO("dlerror: %s", dlerror()); | |
22 | return false; |
0 | # Copyright 1999-2017 Gentoo Foundation | |
1 | # Distributed under the terms of the GNU General Public License v2 | |
2 | ||
3 | # Configuration for /etc/init.d/squeezelite | |
4 | ||
5 | # IP address of Logitech Media Server; leave this blank to try to | |
6 | # locate the server via auto-discovery. | |
7 | # If you fill this in then include "-s" before the IP address, eg: | |
8 | # SL_SERVER_IP="-s 1.2.3.4" | |
9 | SL_SERVERIP="" | |
10 | ||
11 | # User that Squeezelite should run as. The dedicated 'squeezelite' | |
12 | # user is preferred to avoid running with high privilege. This user | |
13 | # should be a member of the 'audio' group to allow access to the audio | |
14 | # hardware. Running as the 'root' user allows the sound output thread | |
15 | # to run at a very high priority -- this can help avoid gaps in | |
16 | # playback, but could be a potential security problem if there are | |
17 | # exploitable vulnerabilities in Squeezelite. | |
18 | SL_USER=squeezelite | |
19 | ||
20 | # Any other switches to pass to Squeezelite. See 'squeezelite -h' for | |
21 | # a description of all possible switches. | |
22 | ||
23 | # Example setting: | |
24 | # 1. the ALSA output device | |
25 | # 2. the player name | |
26 | # 3. turning on visualiser support (-v) | |
27 | # | |
28 | # SL_OPTS="-o sysdefault -n $HOSTNAME -v" | |
29 | # | |
30 | SL_OPTS="" |
0 | #!/sbin/openrc-run | |
1 | # Copyright 1999-2015 Gentoo Foundation | |
2 | # Distributed under the terms of the GNU General Public License v2 | |
3 | ||
4 | depend() { | |
5 | need net | |
6 | use alsasound | |
7 | after bootmisc | |
8 | } | |
9 | ||
10 | start() { | |
11 | ebegin "Starting squeezelite" | |
12 | start-stop-daemon \ | |
13 | --start \ | |
14 | --exec /usr/bin/squeezelite \ | |
15 | --pidfile /run/squeezelite.pid \ | |
16 | --make-pidfile \ | |
17 | --user ${SL_USER} \ | |
18 | --background \ | |
19 | -- ${SL_OPTS} ${SL_SERVERIP} | |
20 | eend $? | |
21 | } | |
22 | ||
23 | stop() { | |
24 | ebegin "Stopping squeezelite" | |
25 | start-stop-daemon \ | |
26 | --stop \ | |
27 | --exec /usr/bin/squeezelite \ | |
28 | --pidfile /run/squeezelite.pid | |
29 | eend $? | |
30 | } |
0 | #!/bin/sh | |
1 | ||
2 | adduser -S -D -H -h /dev/null -s /sbin/nologin -G audio -g squeezelite squeezelite 2>/dev/null | |
3 | ||
4 | exit 0 |
1 | 1 | * Squeezelite - lightweight headless squeezebox emulator |
2 | 2 | * |
3 | 3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.com |
4 | * Ralph Irving 2015-2017, ralph_irving@hotmail.com | |
4 | 5 | * |
5 | 6 | * This program is free software: you can redistribute it and/or modify |
6 | 7 | * it under the terms of the GNU General Public License as published by |
92 | 93 | buf->base_size = size; |
93 | 94 | } |
94 | 95 | |
96 | void _buf_unwrap(struct buffer *buf, size_t cont) { | |
97 | ssize_t len, by = cont - (buf->wrap - buf->readp); | |
98 | size_t size; | |
99 | u8_t *scratch; | |
100 | ||
101 | // do nothing if we have enough space | |
102 | if (by <= 0 || cont >= buf->size) return; | |
103 | ||
104 | // buffer already unwrapped, just move it up | |
105 | if (buf->writep >= buf->readp) { | |
106 | memmove(buf->readp - by, buf->readp, buf->writep - buf->readp); | |
107 | buf->readp -= by; | |
108 | buf->writep -= by; | |
109 | return; | |
110 | } | |
111 | ||
112 | // how much is overlapping | |
113 | size = by - (buf->readp - buf->writep); | |
114 | len = buf->writep - buf->buf; | |
115 | ||
116 | // buffer is wrapped and enough free space to move data up directly | |
117 | if (size <= 0) { | |
118 | memmove(buf->readp - by, buf->readp, buf->wrap - buf->readp); | |
119 | buf->readp -= by; | |
120 | memcpy(buf->wrap - by, buf->buf, min(len, by)); | |
121 | if (len > by) { | |
122 | memmove(buf->buf, buf->buf + by, len - by); | |
123 | buf->writep -= by; | |
124 | } else buf->writep += buf->size - by; | |
125 | return; | |
126 | } | |
127 | ||
128 | scratch = malloc(size); | |
129 | ||
130 | // buffer is wrapped but not enough free room => use scratch zone | |
131 | if (scratch) { | |
132 | memcpy(scratch, buf->writep - size, size); | |
133 | memmove(buf->readp - by, buf->readp, buf->wrap - buf->readp); | |
134 | buf->readp -= by; | |
135 | memcpy(buf->wrap - by, buf->buf, by); | |
136 | memmove(buf->buf, buf->buf + by, len - by - size); | |
137 | buf->writep -= by; | |
138 | memcpy(buf->writep - size, scratch, size); | |
139 | free(scratch); | |
140 | } else { | |
141 | _buf_unwrap(buf, cont / 2); | |
142 | _buf_unwrap(buf, cont - cont / 2); | |
143 | } | |
144 | } | |
145 | ||
95 | 146 | void buf_init(struct buffer *buf, size_t size) { |
96 | 147 | buf->buf = malloc(size); |
97 | 148 | buf->readp = buf->buf; |
0 | /* | |
1 | * Copyright (c) 2013 Ralph Irving | |
2 | * | |
3 | * daemonize.c is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License as published by | |
5 | * the Free Software Foundation; either version 2 of the License, or | |
6 | * (at your option) any later version. | |
7 | * | |
8 | * daemonize.c is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with SlimProtoLib; if not, write to the Free Software | |
15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
16 | */ | |
17 | ||
18 | #include "squeezelite.h" | |
19 | ||
20 | #include <syslog.h> | |
21 | #include <sys/stat.h> | |
22 | ||
23 | void fork_child_handler(int signum) | |
24 | { | |
25 | switch(signum) { | |
26 | case SIGALRM: exit(EXIT_FAILURE); break; | |
27 | case SIGUSR1: exit(EXIT_SUCCESS); break; | |
28 | case SIGCHLD: exit(EXIT_FAILURE); break; | |
29 | } | |
30 | } | |
31 | ||
32 | pid_t parent; | |
33 | void init_daemonize() | |
34 | { | |
35 | pid_t pid, sid; | |
36 | ||
37 | /* already a daemon */ | |
38 | if ( getppid() == 1 ) return; | |
39 | ||
40 | /* Trap signals that we expect to recieve */ | |
41 | signal(SIGCHLD,fork_child_handler); | |
42 | signal(SIGUSR1,fork_child_handler); | |
43 | signal(SIGALRM,fork_child_handler); | |
44 | ||
45 | /* Fork off the parent process */ | |
46 | pid = fork(); | |
47 | if (pid < 0) { | |
48 | syslog( LOG_ERR, "unable to fork daemon, code=%d (%s)", | |
49 | errno, strerror(errno) ); | |
50 | exit(EXIT_FAILURE); | |
51 | } | |
52 | ||
53 | /* If we got a good PID, then we can exit the parent process. */ | |
54 | if (pid > 0) { | |
55 | ||
56 | /* Wait for confirmation from the child via SIGTERM or SIGCHLD, or | |
57 | for two seconds to elapse (SIGALRM). pause() should not return. */ | |
58 | alarm(2); | |
59 | pause(); | |
60 | ||
61 | exit(EXIT_FAILURE); | |
62 | } | |
63 | ||
64 | /* At this point we are executing as the child process */ | |
65 | parent = getppid(); | |
66 | ||
67 | /* Cancel certain signals */ | |
68 | signal(SIGCHLD,SIG_DFL); /* A child process dies */ | |
69 | signal(SIGTSTP,SIG_IGN); /* Various TTY signals */ | |
70 | signal(SIGTTOU,SIG_IGN); | |
71 | signal(SIGTTIN,SIG_IGN); | |
72 | signal(SIGHUP, SIG_IGN); /* Ignore hangup signal */ | |
73 | signal(SIGTERM,SIG_DFL); /* Die on SIGTERM */ | |
74 | ||
75 | /* Change the file mode mask */ | |
76 | umask(0); | |
77 | ||
78 | /* Create a new SID for the child process */ | |
79 | sid = setsid(); | |
80 | if (sid < 0) { | |
81 | syslog( LOG_ERR, "unable to create a new session, code %d (%s)", | |
82 | errno, strerror(errno) ); | |
83 | exit(EXIT_FAILURE); | |
84 | } | |
85 | ||
86 | /* Change the current working directory. This prevents the current | |
87 | directory from being locked; hence not being able to remove it. */ | |
88 | if ((chdir("/")) < 0) { | |
89 | syslog( LOG_ERR, "unable to change directory to %s, code %d (%s)", | |
90 | "/", errno, strerror(errno) ); | |
91 | exit(EXIT_FAILURE); | |
92 | } | |
93 | } | |
94 | ||
95 | int daemon( int nochdir, int noclose ) { | |
96 | ||
97 | if ( ! noclose ) | |
98 | { | |
99 | /* Redirect standard files to /dev/null */ | |
100 | freopen( "/dev/null", "r", stdin); | |
101 | freopen( "/dev/null", "w", stdout); | |
102 | freopen( "/dev/null", "w", stderr); | |
103 | } | |
104 | ||
105 | /* Tell the parent process that we are A-okay */ | |
106 | kill( parent, SIGUSR1 ); | |
107 | ||
108 | return 0; | |
109 | } |
1 | 1 | * Squeezelite - lightweight headless squeezebox emulator |
2 | 2 | * |
3 | 3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.com |
4 | * Ralph Irving 2015-2017, ralph_irving@hotmail.com | |
4 | 5 | * |
5 | 6 | * This program is free software: you can redistribute it and/or modify |
6 | 7 | * it under the terms of the GNU General Public License as published by |
118 | 119 | return 0; |
119 | 120 | } |
120 | 121 | |
122 | static void sort_codecs(int pry, struct codec* ptr) { | |
123 | static int priority[MAX_CODECS]; | |
124 | int i, tpry; | |
125 | struct codec* tptr; | |
126 | ||
127 | for (i = 0; i < MAX_CODECS; i++) { | |
128 | if (!codecs[i]) { | |
129 | codecs[i] = ptr; | |
130 | priority[i] = pry; | |
131 | return; | |
132 | } | |
133 | if (pry < priority[i]) { | |
134 | tptr = codecs[i]; | |
135 | codecs[i] = ptr; | |
136 | ptr = tptr; | |
137 | tpry = priority[i]; | |
138 | priority[i] = pry; | |
139 | pry = tpry; | |
140 | } | |
141 | } | |
142 | } | |
143 | ||
121 | 144 | static thread_type thread; |
122 | 145 | |
123 | 146 | void decode_init(log_level level, const char *include_codecs, const char *exclude_codecs) { |
124 | 147 | int i; |
148 | char* order_codecs; | |
125 | 149 | |
126 | 150 | loglevel = level; |
127 | 151 | |
128 | LOG_INFO("init decode, include codecs: %s exclude codecs: %s", include_codecs ? include_codecs : "", exclude_codecs); | |
152 | LOG_INFO("init decode"); | |
129 | 153 | |
130 | 154 | // register codecs |
131 | 155 | // dsf,dff,alc,wma,wmap,wmal,aac,spt,ogg,ogf,flc,aif,pcm,mp3 |
132 | 156 | i = 0; |
133 | 157 | #if DSD |
134 | if (!strstr(exclude_codecs, "dsd") && (!include_codecs || strstr(include_codecs, "dsd"))) codecs[i++] = register_dsd(); | |
135 | #endif | |
136 | #if FFMPEG | |
137 | if (!strstr(exclude_codecs, "alac") && (!include_codecs || strstr(include_codecs, "alac"))) codecs[i++] = register_ff("alc"); | |
138 | if (!strstr(exclude_codecs, "wma") && (!include_codecs || strstr(include_codecs, "wma"))) codecs[i++] = register_ff("wma"); | |
139 | #endif | |
140 | if (!strstr(exclude_codecs, "aac") && (!include_codecs || strstr(include_codecs, "aac"))) codecs[i++] = register_faad(); | |
141 | if (!strstr(exclude_codecs, "ogg") && (!include_codecs || strstr(include_codecs, "ogg"))) codecs[i++] = register_vorbis(); | |
142 | if (!strstr(exclude_codecs, "flac") && (!include_codecs || strstr(include_codecs, "flac"))) codecs[i++] = register_flac(); | |
143 | if (!strstr(exclude_codecs, "pcm") && (!include_codecs || strstr(include_codecs, "pcm"))) codecs[i++] = register_pcm(); | |
158 | if (!strstr(exclude_codecs, "dsd") && (!include_codecs || (order_codecs = strstr(include_codecs, "dsd")))) | |
159 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_dsd()); | |
160 | #endif | |
161 | #if ALAC | |
162 | if (!strstr(exclude_codecs, "alac") && (!include_codecs || (order_codecs = strstr(include_codecs, "alac")))) | |
163 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_alac()); | |
164 | #elif FFMPEG | |
165 | if (!strstr(exclude_codecs, "alac") && (!include_codecs || (order_codecs = strstr(include_codecs, "alac")))) | |
166 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_ff("alc")); | |
167 | if (!strstr(exclude_codecs, "wma") && (!include_codecs || (order_codecs = strstr(include_codecs, "wma")))) | |
168 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_ff("wma")); | |
169 | #endif | |
170 | #ifndef NO_FAAD | |
171 | if (!strstr(exclude_codecs, "aac") && (!include_codecs || (order_codecs = strstr(include_codecs, "aac")))) | |
172 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_faad()); | |
173 | #endif | |
174 | if (!strstr(exclude_codecs, "ogg") && (!include_codecs || (order_codecs = strstr(include_codecs, "ogg")))) | |
175 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_vorbis()); | |
176 | #if OPUS | |
177 | if (!strstr(exclude_codecs, "ops") && (!include_codecs || (order_codecs = strstr(include_codecs, "ops")))) | |
178 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_opus()); | |
179 | #endif | |
180 | if (!strstr(exclude_codecs, "flac") && (!include_codecs || (order_codecs = strstr(include_codecs, "flac")))) | |
181 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_flac()); | |
182 | if (!strstr(exclude_codecs, "pcm") && (!include_codecs || (order_codecs = strstr(include_codecs, "pcm")))) | |
183 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_pcm()); | |
144 | 184 | |
145 | 185 | // try mad then mpg for mp3 unless command line option passed |
146 | 186 | if (!(strstr(exclude_codecs, "mp3") || strstr(exclude_codecs, "mad")) && |
147 | (!include_codecs || strstr(include_codecs, "mp3") || strstr(include_codecs, "mad"))) codecs[i] = register_mad(); | |
148 | if (!(strstr(exclude_codecs, "mp3") || strstr(exclude_codecs, "mpg")) && !codecs[i] && | |
149 | (!include_codecs || strstr(include_codecs, "mp3") || strstr(include_codecs, "mpg"))) codecs[i] = register_mpg(); | |
187 | (!include_codecs || (order_codecs = strstr(include_codecs, "mp3")) || (order_codecs = strstr(include_codecs, "mad")))) | |
188 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_mad()); | |
189 | else if (!(strstr(exclude_codecs, "mp3") || strstr(exclude_codecs, "mpg")) && | |
190 | (!include_codecs || (order_codecs = strstr(include_codecs, "mp3")) || (order_codecs = strstr(include_codecs, "mpg")))) | |
191 | sort_codecs((include_codecs ? order_codecs - include_codecs : i), register_mpg()); | |
192 | ||
193 | LOG_DEBUG("include codecs: %s exclude codecs: %s", include_codecs ? include_codecs : "", exclude_codecs); | |
150 | 194 | |
151 | 195 | mutex_create(decode.mutex); |
152 | 196 | |
153 | 197 | #if LINUX || OSX || FREEBSD |
154 | 198 | pthread_attr_t attr; |
155 | 199 | pthread_attr_init(&attr); |
200 | #ifdef PTHREAD_STACK_MIN | |
156 | 201 | pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + DECODE_THREAD_STACK_SIZE); |
202 | #endif | |
157 | 203 | pthread_create(&thread, &attr, decode_thread, NULL); |
158 | 204 | pthread_attr_destroy(&attr); |
159 | 205 | #endif |
0 | .\" Hey, EMACS: -*- nroff -*- | |
1 | .\" (C) Copyright 2013-4 Chris Boot <bootc@debian.org> | |
2 | .\" | |
3 | .\" First parameter, NAME, should be all caps | |
4 | .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection | |
5 | .\" other parameters are allowed: see man(7), man(1) | |
6 | .TH SQUEEZELITE 1 "2020-07-16" "Debian Project" | |
7 | .\" Please adjust this date whenever revising the manpage. | |
8 | .\" | |
9 | .\" Some roff macros, for reference: | |
10 | .\" .nh disable hyphenation | |
11 | .\" .hy enable hyphenation | |
12 | .\" .ad l left justify | |
13 | .\" .ad b justify to both left and right margins | |
14 | .\" .nf disable filling | |
15 | .\" .fi enable filling | |
16 | .\" .br insert line break | |
17 | .\" .sp <n> insert n+1 empty lines | |
18 | .\" for manpage-specific macros, see man(7) | |
19 | .SH NAME | |
20 | squeezelite \- Lightweight headless Squeezebox emulator | |
21 | .SH SYNOPSIS | |
22 | .B squeezelite | |
23 | .RI [ options ] | |
24 | .SH DESCRIPTION | |
25 | .B Squeezelite | |
26 | is a small headless Logitech Squeezebox emulator. It is aimed at supporting high | |
27 | quality audio including USB DAC based output at multiple sample rates. | |
28 | .PP | |
29 | The player is controlled using, and media is streamed from, a Logitech Media | |
30 | Server instance running somewhere on the local network. | |
31 | .SH OPTIONS | |
32 | This program supports the following options: | |
33 | .TP | |
34 | .B \-? | |
35 | Show a summary of the available command-line options. | |
36 | .TP | |
37 | .B \-s <server>[:<port>] | |
38 | Connect to the specified Logitech Media Server, otherwise uses automatic | |
39 | discovery to find server on the local network. This option should only be needed | |
40 | if automatic discovery does not work, or the server is not on the local network | |
41 | segment (e.g. behind a router). | |
42 | .TP | |
43 | .B \-o <output device> | |
44 | Specify the audio output device; the default value is | |
45 | .IR default . | |
46 | Use the | |
47 | .B \-l | |
48 | option to list available output devices. | |
49 | .I - | |
50 | can be used to output raw samples to standard output. | |
51 | .TP | |
52 | .B \-l | |
53 | List available audio output devices to stdout and exit. These device names can | |
54 | be passed to the | |
55 | .B \-o | |
56 | option in order to select a particular device or configuration to use for audio | |
57 | playback. | |
58 | .TP | |
59 | .B \-a <params> | |
60 | Specify parameters used when opening an audio output device. | |
61 | .PP | |
62 | .RS | |
63 | For ALSA, the format | |
64 | .B <b>:<p>:<f>:<m>:<d> | |
65 | is used where | |
66 | .B <b> | |
67 | is the buffer time in milliseconds (values less than 500) or size in bytes (default | |
68 | .IR 40 ms); | |
69 | .B <p> | |
70 | is the period count (values less than 50) or size in bytes (default | |
71 | .IR 4 " periods);" | |
72 | .B <f> | |
73 | is the sample format (possible values: | |
74 | .IR 16 ", " 24 ", " 24_3 " or " 32 ); | |
75 | .B <m> | |
76 | is whether to use mmap (possible values: | |
77 | .IR 0 " or " 1 ). | |
78 | .B <d> | |
79 | open ALSA output device twice. (possible values: | |
80 | .IR 0 " or " 1 ). | |
81 | .RE | |
82 | .RS | |
83 | .PP | |
84 | For Linux PortAudio, the value | |
85 | .B <l> | |
86 | is simply the target latency in milliseconds. | |
87 | .RE | |
88 | .RS | |
89 | .PP | |
90 | For MacOS, | |
91 | .B <l>:<r> | |
92 | .B <l> | |
93 | is target latency in milliseconds. | |
94 | .B <r> | |
95 | open device in Pro Mode or Play Nice (respective values: | |
96 | .IR 0 " or " 1 ). | |
97 | .RE | |
98 | .RS | |
99 | .PP | |
100 | For Windows, | |
101 | .B <l>:<e> | |
102 | .B <l> | |
103 | is target latency in milliseconds. | |
104 | .B <e> | |
105 | use exclusive mode for WASAPI (possible values: | |
106 | .IR 0 " or " 1 ). | |
107 | .RE | |
108 | .RS | |
109 | .PP | |
110 | When the output is sent to standard output, the value can be | |
111 | .IR 16 ", " 24 " or " 32 , | |
112 | which denotes the sample size in bits. Little Endian only. | |
113 | .RE | |
114 | .TP | |
115 | .B \-b <stream>:<output> | |
116 | Specify internal stream and output buffer sizes in kilobytes. Default is 2048:3446. | |
117 | .TP | |
118 | .B \-c <codec1>,... | |
119 | Restrict codecs to those specified, otherwise load all available codecs. Use | |
120 | .B squeezelite -? | |
121 | to obtain the list of codecs built into \fBsqueezelite\fR. | |
122 | .TP | |
123 | .B \-C <timeout> | |
124 | Close the output device after | |
125 | .B <timeout> | |
126 | seconds of the player being idle; the default is to always keep the device open | |
127 | as long as the payer is "on". | |
128 | .TP | |
129 | .B \-d <category>=<level> | |
130 | Set logging level. Categories are: | |
131 | .IR all ", " slimproto ", " stream ", " decode ", " output " or " ir . | |
132 | Levels can be: | |
133 | .IR info ", " debug " or " sdebug . | |
134 | The option can be repeated to set different log levels for different categories. | |
135 | .TP | |
136 | .B \-e <codec1>,... | |
137 | Explicitly exclude native support of one or more codecs. See also | |
138 | .BR \-c , | |
139 | above. | |
140 | .TP | |
141 | .B \-f <logfile> | |
142 | Send logging output to a log file instead of standard output or standard error. | |
143 | .TP | |
144 | .B \-G <Rpi GPIO#>:<H/L> | |
145 | Specify the BCM GPIO# to use for Amp Power Relay and if the output | |
146 | should be Active High or Low. This cannot be used with the \fB-S\fR option. | |
147 | .TP | |
148 | .B \-i [<filename>] | |
149 | Enable LIRC remote control support. If the optional | |
150 | .B <filename> | |
151 | is not provided, | |
152 | .I ~/.lircrc | |
153 | is used instead. | |
154 | .TP | |
155 | .B \-m <mac addr> | |
156 | Override the player's MAC address. The format must be colon-delimited | |
157 | hexadecimal, for example: ab:cd:ef:12:34:56. This is usually automatically | |
158 | detected, and should not need to be provided in most circumstances. | |
159 | .TP | |
160 | .B \-M <modelname> | |
161 | Override the player's hardware model name. The default value is | |
162 | .IR SqueezeLite . | |
163 | .TP | |
164 | .B \-n <name> | |
165 | Set the player name. This name is used by the Logitech Media Server to refer to | |
166 | the player by name. This option is mututally exclusive with | |
167 | .BR \-N . | |
168 | .TP | |
169 | .B \-N <filename> | |
170 | Allow the server to set the player's name. The player name is stored in the file | |
171 | pointed to by | |
172 | .B <filename> | |
173 | so that it can persist between restarts. This option is mututally exclusive with | |
174 | .BR \-n . | |
175 | .TP | |
176 | .B \-O <mixer device> | |
177 | Specify mixer device, defaults to \fB<output device>\fR. | |
178 | \. | |
179 | .TP | |
180 | .B \-p <priority> | |
181 | Set real time priority of output thread (1-99; default | |
182 | .IR 45 ). | |
183 | Not applicable when using PortAudio. | |
184 | .TP | |
185 | .B \-P <filename> | |
186 | Write the process ID (PID) number to the given | |
187 | .BR <filename> . | |
188 | This may be useful when running \fBsqueezelite\fR as a daemon. | |
189 | .TP | |
190 | .B \-r <rates>[:<delay>] | |
191 | Specify sample rates supported by the output device; this is required if the | |
192 | output device is switched off when \fBsqueezelite\fR is started. The format is | |
193 | either a single maximum sample rate, a range of sample rates in the format | |
194 | .IR <min> - <max> , | |
195 | or a comma-separated list of available rates. Delay is an optional time to wait | |
196 | when switching sample rates between tracks, in milliseconds. | |
197 | .TP | |
198 | .B \-S <power script> | |
199 | Absolute path to script to launch on power commands from LMS. This | |
200 | cannot be used with the \fB-G\fR option. | |
201 | .TP | |
202 | .B \-u|-R [params] | |
203 | Enable upsampling of played audio. The argument is optional; see | |
204 | .B RESAMPLING | |
205 | (below) for more information. The options | |
206 | .BR -u " and " -R | |
207 | are synonymous. | |
208 | .TP | |
209 | .B \-D [delay] | |
210 | Output device supports DSD over PCM (DoP). DSD streams will be converted to DoP | |
211 | before output. If this option is not supplied, DSD streams will be converted to | |
212 | PCM and resampled, so they can be played on a PCM DAC. Delay is an optional time | |
213 | to wait when switching between PCM and DoP between tracks, in milliseconds. | |
214 | .TP | |
215 | .B \-v | |
216 | Enable visualiser support. This creates a shared memory segment that contains | |
217 | some of the audio being played, so that an external visualiser can read and | |
218 | process this to create visualisations. | |
219 | .TP | |
220 | .B \-W | |
221 | Read wave and aiff format from header, ignoring server parameters. | |
222 | .TP | |
223 | .B \-L | |
224 | List available volume controls for the output device. Only applicable when | |
225 | using ALSA output. | |
226 | .TP | |
227 | .B \-U <control> | |
228 | Unmute the given ALSA | |
229 | .B <control> | |
230 | at daemon startup and set it to full volume. Use software volume adjustment for | |
231 | playback. This option is mutually exclusive with the \fB\-V\fR option. Only | |
232 | applicable when using ALSA output. | |
233 | .TP | |
234 | .B \-V <control> | |
235 | Use the given ALSA | |
236 | .B <control> | |
237 | for volume adjustment during playback. This prevents the use of software volume | |
238 | control within \fBsqueezelite\fR. This option is mutually exclusive with the | |
239 | \fB\-U\fR option. If neither \fB\-U\fR nor \fB\-V\fR options are provided, | |
240 | no ALSA controls are adjusted while running \fBsqueezelite\fR and software | |
241 | volume control is used instead. Only applicable when using ALSA output. | |
242 | .TP | |
243 | .B \-X | |
244 | Use linear volume adjustments instead of in terms of dB (only for | |
245 | hardware volume control). | |
246 | .TP | |
247 | .B \-z | |
248 | Cause \fBsqueezelite\fR to run as a daemon. That is, it detaches itself from the | |
249 | terminal and runs in the background. | |
250 | .TP | |
251 | .B \-Z <rate> | |
252 | Report rate to server in helo as the maximum sample rate we can support. | |
253 | .TP | |
254 | .B \-t | |
255 | Display version and license information. | |
256 | .SH RESAMPLING | |
257 | Audio can be resampled or upsampled before being sent to the output device. This | |
258 | can be enabled simply by passing the \fB\-u\fR option to \fBsqueezelite\fR, but | |
259 | further configuration can be given as an argument to the option. | |
260 | .PP | |
261 | Resampling is performed using the SoX Resampler library; the documentation for | |
262 | that library and the SoX \fIrate\fR effect many be helpful when configuring | |
263 | upsampling for \fBsqueezelite\fR. | |
264 | .PP | |
265 | The format of the argument is | |
266 | .B <recipe>:<flags>:<attenuation>:<precision>:<passband_end>:<stopband_start>:<phase_response> | |
267 | .SS recipe | |
268 | This part of the argument string is made up of a number of single-character | |
269 | flags: \fB[v|h|m|l|q][L|I|M][s][E|X]\fR. The default value is \fBhL\fR. | |
270 | .TP | |
271 | .IR v ", " h ", " m ", " l " or " q | |
272 | are mutually exclusive and correspond to very high, high, medium, low or quick | |
273 | quality. | |
274 | .TP | |
275 | .IR L ", " I " or " M | |
276 | correspond to linear, intermediate or minimum phase. | |
277 | .TP | |
278 | .IR s | |
279 | changes resampling bandwidth from the default 95% (based on the 3dB point) to | |
280 | 99%. | |
281 | .TP | |
282 | .IR E | |
283 | exception - avoids resampling if the output device supports the playback sample | |
284 | rate natively. | |
285 | .TP | |
286 | .IR X | |
287 | resamples to the maximum sample rate for the output device ("asynchronous" | |
288 | resampling). | |
289 | .TP | |
290 | .B Examples | |
291 | .B \-u vLs | |
292 | would use very high quality setting, linear phase filter and steep cut-off. | |
293 | .br | |
294 | .B \-u hM | |
295 | would specify high quality, with the minimum phase filter. | |
296 | .br | |
297 | .B \-u hMX | |
298 | would specify high quality, with the minimum phase filter and async upsampling | |
299 | to max device rate. | |
300 | .SS flags | |
301 | The second optional argument to \fB\-u\fR allows the user to specify the | |
302 | following arguments (taken from the \fIsoxr.h\fR header file), in hex: | |
303 | .sp | |
304 | #define SOXR_ROLLOFF_SMALL 0u /* <= 0.01 dB */ | |
305 | .br | |
306 | #define SOXR_ROLLOFF_MEDIUM 1u /* <= 0.35 dB */ | |
307 | .br | |
308 | #define SOXR_ROLLOFF_NONE 2u /* For Chebyshev bandwidth. */ | |
309 | .sp | |
310 | #define SOXR_MAINTAIN_3DB_PT 4u /* Reserved for internal use. */ | |
311 | .br | |
312 | #define SOXR_HI_PREC_CLOCK 8u /* Increase 'irrational' ratio accuracy. */ | |
313 | .br | |
314 | #define SOXR_DOUBLE_PRECISION 16u /* Use D.P. calcs even if precision <= 20. */ | |
315 | .br | |
316 | #define SOXR_VR 32u /* Experimental, variable-rate resampling. */ | |
317 | .TP | |
318 | .B Examples | |
319 | .B \-u :2 | |
320 | would specify \fBSOXR_ROLLOFF_NONE\fR. | |
321 | .sp | |
322 | \fBNB:\fR In the example above the first option, \fB<quality>\fR, has not been | |
323 | specified so would default to \fBhL\fR. Therefore, specifying \fB\-u :2\fR is | |
324 | equivalent to having specified \fB\-u hL:2\fR. | |
325 | .SS attenuation | |
326 | Internally, data is passed to the SoX resample process as 32 bit integers and | |
327 | output from the SoX resample process as 32 bit integers. Why does this matter? | |
328 | There is the possibility that integer samples, once resampled may be clipped | |
329 | (i.e. exceed the maximum value). By default, if you do not specify an | |
330 | \fBattenuation\fR value, it will default to \-1db. A value of \fI0\fR on the | |
331 | command line, i.e. \fB-u ::0\fR will disable the default \-1db attenuation being | |
332 | applied. | |
333 | .sp | |
334 | \fBNB:\fR Clipped samples will be logged. Keep an eye on the log file. | |
335 | .TP | |
336 | .B Examples | |
337 | .B \-u ::6 | |
338 | specifies to apply \-6db (ie. halve the volume) prior to the resampling process. | |
339 | .SS precision | |
340 | The internal 'bit' precision used in the re-sampling calculations (ie. quality). | |
341 | .sp | |
342 | \fBNB:\fR HQ = 20, VHQ = 28. | |
343 | .TP | |
344 | .B Examples | |
345 | .B \-u :::28 | |
346 | specifies 28-bit precision. | |
347 | .SS passband_end | |
348 | A percentage value between 0 and 100, where 100 is the Nyquist frequency. The | |
349 | default if not explicitly set is \fI91.3\fR. | |
350 | .TP | |
351 | .B Examples | |
352 | .B \-u ::::98 | |
353 | specifies passband ends at 98 percent of the Nyquist frequency. | |
354 | .SS stopband_start | |
355 | A percentage value between 0 and 100, where 100 is the Nyquist frequency. The | |
356 | default if not explicitly set is \fI100\fR. | |
357 | .TP | |
358 | .B Examples | |
359 | .B \-u :::::100 | |
360 | specifies that the stopband starts at the Nyquist frequency. | |
361 | .SS phase_response | |
362 | A value between 0-100, where \fI0\fR is equivalent to the recipe \fIM\fR flag | |
363 | for minimum phase, \fI25\fR is equivalent to the recipe \fII\fR flag for | |
364 | intermediate phase and \fI50\fR is equivalent to the recipe \fIL\fR flag for | |
365 | linear phase. | |
366 | .TP | |
367 | .B Examples | |
368 | .B \-u ::::::50 | |
369 | specifies linear phase. | |
370 | .SH SEE ALSO | |
371 | .TP | |
372 | http://wiki.slimdevices.com/index.php/Squeezelite | |
373 | .TP | |
374 | http://wiki.slimdevices.com/index.php/Logitech_Media_Server | |
375 | .TP | |
376 | sox(1) | |
377 | for further information about resampling. |
29 | 29 | #define LOCK_O mutex_lock(outputbuf->mutex) |
30 | 30 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) |
31 | 31 | |
32 | // check for 32 dop marker frames to see if this is dop in flac | |
33 | // dop is always encoded in 24 bit samples with marker 0x0005xxxx or 0x00FAxxxx | |
34 | bool is_flac_dop(u32_t *lptr, u32_t *rptr, frames_t frames) { | |
32 | // check for 32 dop marker frames to see if this is a dop stream | |
33 | // dop is always encoded in 24 bit samples with markers 0x05 or 0xFA in MSB | |
34 | bool is_stream_dop(u8_t *lptr, u8_t *rptr, int step, frames_t frames) { | |
35 | 35 | int matched = 0; |
36 | 36 | u32_t next = 0; |
37 | 37 | |
38 | 38 | while (frames--) { |
39 | if (((*lptr & 0x00FF0000) == 0x00050000 && (*rptr & 0x00FF0000) == 0x00050000) || | |
40 | ((*lptr & 0x00FF0000) == 0x00FA0000 && (*rptr & 0x00FF0000) == 0x00FA0000)) { | |
41 | if (*lptr >> 24 == next) { | |
39 | if ((*lptr == 0x05 && *rptr == 0x05) || | |
40 | (*lptr == 0xFA && *rptr == 0xFA)) { | |
41 | if (*lptr == next) { | |
42 | 42 | matched++; |
43 | next = ( 0x05 + 0xFA ) - next; | |
44 | 43 | } else { |
45 | next = *lptr >> 24; | |
44 | next = *lptr; | |
46 | 45 | matched = 1; |
47 | 46 | } |
47 | next = ( 0x05 + 0xFA ) - next; | |
48 | 48 | } else { |
49 | 49 | return false; |
50 | 50 | } |
52 | 52 | return true; |
53 | 53 | } |
54 | 54 | |
55 | ++lptr; ++rptr; | |
55 | lptr+=step; rptr+=step; | |
56 | 56 | } |
57 | 57 | return false; |
58 | 58 | } |
64 | 64 | if (!invert) { |
65 | 65 | while (frames--) { |
66 | 66 | u32_t scaled_marker = marker << 24; |
67 | *ptr = (*ptr & 0x00FFFFFF) | scaled_marker; | |
67 | *ptr = (*ptr & 0x00FFFF00) | scaled_marker; | |
68 | 68 | ++ptr; |
69 | *ptr = (*ptr & 0x00FFFFFF) | scaled_marker; | |
69 | *ptr = (*ptr & 0x00FFFF00) | scaled_marker; | |
70 | 70 | ++ptr; |
71 | 71 | marker = ( 0x05 + 0xFA ) - marker; |
72 | 72 | } |
73 | 73 | } else { |
74 | 74 | while (frames--) { |
75 | 75 | u32_t scaled_marker = marker << 24; |
76 | *ptr = ((~(*ptr)) & 0x00FFFFFF) | scaled_marker; | |
76 | *ptr = ((~(*ptr)) & 0x00FFFF00) | scaled_marker; | |
77 | 77 | ++ptr; |
78 | *ptr = ((~(*ptr)) & 0x00FFFFFF) | scaled_marker; | |
78 | *ptr = ((~(*ptr)) & 0x00FFFF00) | scaled_marker; | |
79 | 79 | ++ptr; |
80 | 80 | marker = ( 0x05 + 0xFA ) - marker; |
81 | 81 | } |
82 | 82 | } |
83 | 83 | } |
84 | 84 | |
85 | // fill silence buffer with 10101100 which represents dop silence | |
86 | // leave marker zero it will be updated at output, leave lsb zero | |
87 | void dop_silence_frames(u32_t *ptr, frames_t frames) { | |
88 | while (frames--) { | |
89 | *ptr++ = 0x00ACAC00; | |
90 | *ptr++ = 0x00ACAC00; | |
91 | } | |
92 | } | |
93 | ||
94 | void dop_init(bool enable, unsigned delay) { | |
95 | LOCK_O; | |
96 | output.has_dop = enable; | |
97 | output.dop_delay = delay; | |
98 | UNLOCK_O; | |
99 | } | |
100 | ||
101 | 85 | #endif // DSD |
1 | 1 | * Squeezelite - lightweight headless squeezebox emulator |
2 | 2 | * |
3 | 3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.com |
4 | * Ralph Irving 2015-2017, ralph_irving@hotmail.com | |
4 | 5 | * |
5 | 6 | * This program is free software: you can redistribute it and/or modify |
6 | 7 | * it under the terms of the GNU General Public License as published by |
57 | 58 | |
58 | 59 | #define BLOCK 4096 // expected size of dsd block |
59 | 60 | #define BLOCK_FRAMES BLOCK * BYTES_PER_FRAME |
60 | #define WRAP_BUF_SIZE 16 | |
61 | #define WRAP_BUF_SIZE 32 // max 4 bytes per frame and 8 channels | |
61 | 62 | |
62 | 63 | typedef enum { UNKNOWN=0, DSF, DSDIFF } dsd_type; |
63 | 64 | |
64 | static bool dop = false; // local copy of output.has_dop to avoid holding output lock | |
65 | static dsd_format outfmt = PCM; // local copy of output.dsdfmt to avoid holding output lock | |
65 | 66 | |
66 | 67 | struct dsd { |
67 | 68 | dsd_type type; |
210 | 211 | |
211 | 212 | unsigned bytes = _buf_used(streambuf); |
212 | 213 | unsigned block_left = d->block_size; |
213 | ||
214 | unsigned bytes_per_frame = dop ? 2 : 1; | |
214 | unsigned padding = 0; | |
215 | ||
216 | unsigned bytes_per_frame; | |
217 | switch (outfmt) { | |
218 | case DSD_U32_LE: | |
219 | case DSD_U32_BE: | |
220 | bytes_per_frame = 4; | |
221 | break; | |
222 | case DSD_U16_LE: | |
223 | case DSD_U16_BE: | |
224 | case DOP: | |
225 | case DOP_S24_LE: | |
226 | case DOP_S24_3LE: | |
227 | bytes_per_frame = 2; | |
228 | break; | |
229 | default: | |
230 | bytes_per_frame = 1; | |
231 | } | |
215 | 232 | |
216 | 233 | if (bytes < d->block_size * d->channels) { |
217 | 234 | LOG_INFO("stream too short"); // this can occur when scanning the track |
235 | 252 | iptrr -= streambuf->size; |
236 | 253 | } |
237 | 254 | |
255 | // Remove zero padding from last block in case of inaccurate sample count | |
256 | if ((_buf_used(streambuf) == d->block_size * d->channels) | |
257 | && (d->sample_bytes > _buf_used(streambuf))) { | |
258 | int i; | |
259 | u8_t *ipl, *ipr; | |
260 | for (i = d->block_size - 1; i > 0; i--) { | |
261 | ipl = iptrl + i; | |
262 | if (ipl >= streambuf->wrap) ipl -= streambuf->size; | |
263 | ipr = iptrr + i; | |
264 | if (ipr >= streambuf->wrap) ipr -= streambuf->size; | |
265 | if (*ipl || *ipr) break; | |
266 | padding++; | |
267 | } | |
268 | block_left -= padding; | |
269 | } | |
270 | ||
238 | 271 | bytes = min(block_left, min(streambuf->wrap - iptrl, streambuf->wrap - iptrr)); |
239 | 272 | |
240 | 273 | IF_DIRECT( |
248 | 281 | |
249 | 282 | frames = min(bytes, d->sample_bytes) / bytes_per_frame; |
250 | 283 | if (frames == 0) { |
251 | if (dop && d->sample_bytes == 1 && bytes >= 2) { | |
252 | // 1 byte left add a byte of silence and play | |
253 | *(iptrl + 1) = *(iptrr + 1) = 0x69; | |
284 | if (d->sample_bytes && bytes >= (2 * d->sample_bytes)) { | |
285 | // byte(s) left fill frame with silence byte(s) and play | |
286 | int i; | |
287 | for (i = d->sample_bytes; i < bytes_per_frame; i++) | |
288 | *(iptrl + i) = *(iptrr + i) = 0x69; | |
254 | 289 | frames = 1; |
255 | 290 | } else { |
256 | // should not get here due to wrapping m/2 for dop should never result in 0 as header len is always even | |
291 | // should not get here due to wrapping m/2 for dsd should never result in 0 as header len is always even | |
257 | 292 | LOG_INFO("frames got to zero"); |
258 | 293 | return DECODE_COMPLETE; |
259 | 294 | } |
265 | 300 | |
266 | 301 | count = frames; |
267 | 302 | |
268 | if (dop) { | |
303 | switch (outfmt) { | |
304 | ||
305 | case DSD_U32_LE: | |
306 | case DSD_U32_BE: | |
307 | ||
308 | if (d->channels == 1) { | |
309 | if (d->lsb_first) { | |
310 | while (count--) { | |
311 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16 | |
312 | | dsd2pcm_bitreverse[*(iptrl+2)] << 8 | dsd2pcm_bitreverse[*(iptrl+3)]; | |
313 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16 | |
314 | | dsd2pcm_bitreverse[*(iptrl+2)] << 8 | dsd2pcm_bitreverse[*(iptrl+3)]; | |
315 | iptrl += 4; | |
316 | } | |
317 | } else { | |
318 | while (count--) { | |
319 | *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16 | *(iptrl+2) << 8 | *(iptrl+3); | |
320 | *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16 | *(iptrl+2) << 8 | *(iptrl+3); | |
321 | iptrl += 4; | |
322 | } | |
323 | } | |
324 | } else { | |
325 | if (d->lsb_first) { | |
326 | while (count--) { | |
327 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16 | |
328 | | dsd2pcm_bitreverse[*(iptrl+2)] << 8 | dsd2pcm_bitreverse[*(iptrl+3)]; | |
329 | *(optr++) = dsd2pcm_bitreverse[*(iptrr)] << 24 | dsd2pcm_bitreverse[*(iptrr+1)] << 16 | |
330 | | dsd2pcm_bitreverse[*(iptrr+2)] << 8 | dsd2pcm_bitreverse[*(iptrr+3)]; | |
331 | iptrl += 4; | |
332 | iptrr += 4; | |
333 | } | |
334 | } else { | |
335 | while (count--) { | |
336 | *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16 | *(iptrl+2) << 8 | *(iptrl+3); | |
337 | *(optr++) = *(iptrr) << 24 | *(iptrr+1) << 16 | *(iptrr+2) << 8 | *(iptrr+3); | |
338 | iptrl += 4; | |
339 | iptrr += 4; | |
340 | } | |
341 | } | |
342 | } | |
343 | ||
344 | break; | |
345 | ||
346 | case DSD_U16_LE: | |
347 | case DSD_U16_BE: | |
348 | ||
349 | if (d->channels == 1) { | |
350 | if (d->lsb_first) { | |
351 | while (count--) { | |
352 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16; | |
353 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16; | |
354 | iptrl += 2; | |
355 | } | |
356 | } else { | |
357 | while (count--) { | |
358 | *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16; | |
359 | *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16; | |
360 | iptrl += 2; | |
361 | } | |
362 | } | |
363 | } else { | |
364 | if (d->lsb_first) { | |
365 | while (count--) { | |
366 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24 | dsd2pcm_bitreverse[*(iptrl+1)] << 16; | |
367 | *(optr++) = dsd2pcm_bitreverse[*(iptrr)] << 24 | dsd2pcm_bitreverse[*(iptrr+1)] << 16; | |
368 | iptrl += 2; | |
369 | iptrr += 2; | |
370 | } | |
371 | } else { | |
372 | while (count--) { | |
373 | *(optr++) = *(iptrl) << 24 | *(iptrl+1) << 16; | |
374 | *(optr++) = *(iptrr) << 24 | *(iptrr+1) << 16; | |
375 | iptrl += 2; | |
376 | iptrr += 2; | |
377 | } | |
378 | } | |
379 | } | |
380 | ||
381 | break; | |
382 | ||
383 | case DSD_U8: | |
384 | ||
385 | if (d->channels == 1) { | |
386 | if (d->lsb_first) { | |
387 | while (count--) { | |
388 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24; | |
389 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24; | |
390 | iptrl += 1; | |
391 | } | |
392 | } else { | |
393 | while (count--) { | |
394 | *(optr++) = *(iptrl) << 24; | |
395 | *(optr++) = *(iptrl) << 24; | |
396 | iptrl += 1; | |
397 | } | |
398 | } | |
399 | } else { | |
400 | if (d->lsb_first) { | |
401 | while (count--) { | |
402 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 24; | |
403 | *(optr++) = dsd2pcm_bitreverse[*(iptrr)] << 24; | |
404 | iptrl += 1; | |
405 | iptrr += 1; | |
406 | } | |
407 | } else { | |
408 | while (count--) { | |
409 | *(optr++) = *(iptrl) << 24; | |
410 | *(optr++) = *(iptrr) << 24; | |
411 | iptrl += 1; | |
412 | iptrr += 1; | |
413 | } | |
414 | } | |
415 | } | |
416 | ||
417 | break; | |
418 | ||
419 | case DOP: | |
420 | case DOP_S24_LE: | |
421 | case DOP_S24_3LE: | |
269 | 422 | |
270 | 423 | if (d->channels == 1) { |
271 | 424 | if (d->lsb_first) { |
299 | 452 | } |
300 | 453 | } |
301 | 454 | |
302 | } else { | |
455 | break; | |
456 | ||
457 | case PCM: | |
303 | 458 | |
304 | 459 | if (d->channels == 1) { |
305 | 460 | float *iptrf = d->transfer[0]; |
328 | 483 | } |
329 | 484 | } |
330 | 485 | |
486 | break; | |
487 | ||
331 | 488 | } |
332 | 489 | |
333 | 490 | _buf_inc_readp(streambuf, bytes_read); |
352 | 509 | LOG_SDEBUG("write %u frames", frames); |
353 | 510 | } |
354 | 511 | |
512 | if (padding) { | |
513 | _buf_inc_readp(streambuf, padding); | |
514 | LOG_INFO("Zero padding removed: %u bytes", padding); | |
515 | } | |
516 | ||
355 | 517 | // skip the other channel blocks |
356 | 518 | // the right channel has already been read and is guarenteed to be in streambuf so can be skipped immediately |
357 | 519 | if (d->channels > 1) { |
384 | 546 | out = process.max_in_frames; |
385 | 547 | ); |
386 | 548 | |
387 | if (dop) { | |
549 | switch (outfmt) { | |
550 | case DSD_U32_LE: | |
551 | case DSD_U32_BE: | |
552 | bytes_per_frame = d->channels * 4; | |
553 | break; | |
554 | case DSD_U16_LE: | |
555 | case DSD_U16_BE: | |
556 | case DOP: | |
557 | case DOP_S24_LE: | |
558 | case DOP_S24_3LE: | |
388 | 559 | bytes_per_frame = d->channels * 2; |
389 | } else { | |
560 | break; | |
561 | default: | |
390 | 562 | bytes_per_frame = d->channels; |
391 | 563 | out = min(out, BLOCK); |
392 | 564 | } |
403 | 575 | optr = (u32_t *)process.inbuf; |
404 | 576 | ); |
405 | 577 | |
406 | // handle wrap around end of streambuf and partial dop frame at end of stream | |
578 | // handle wrap around end of streambuf and partial dsd frame at end of stream | |
407 | 579 | if (!frames && bytes < bytes_per_frame) { |
408 | 580 | memset(tmp, 0x69, WRAP_BUF_SIZE); // 0x69 = dsd silence |
409 | 581 | memcpy(tmp, streambuf->readp, bytes); |
419 | 591 | |
420 | 592 | count = frames; |
421 | 593 | |
422 | if (dop) { | |
594 | switch (outfmt) { | |
595 | ||
596 | case DSD_U32_LE: | |
597 | case DSD_U32_BE: | |
598 | ||
599 | if (d->channels == 1) { | |
600 | while (count--) { | |
601 | *(optr++) = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3); | |
602 | *(optr++) = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3); | |
603 | iptr += bytes_per_frame; | |
604 | } | |
605 | } else { | |
606 | while (count--) { | |
607 | *(optr++) = *(iptr ) << 24 | *(iptr + d->channels) << 16 | |
608 | | *(iptr + 2 * d->channels) << 8 | *(iptr + 3 * d->channels); | |
609 | *(optr++) = *(iptr+1) << 24 | *(iptr + d->channels + 1) << 16 | |
610 | | *(iptr + 2 * d->channels + 1) << 8 | *(iptr + 3 * d->channels + 1); | |
611 | iptr += bytes_per_frame; | |
612 | } | |
613 | } | |
614 | ||
615 | break; | |
616 | ||
617 | case DSD_U16_LE: | |
618 | case DSD_U16_BE: | |
619 | ||
620 | if (d->channels == 1) { | |
621 | while (count--) { | |
622 | *(optr++) = *(iptr) << 24 | *(iptr+1) << 16; | |
623 | *(optr++) = *(iptr) << 24 | *(iptr+1) << 16; | |
624 | iptr += bytes_per_frame; | |
625 | } | |
626 | } else { | |
627 | while (count--) { | |
628 | *(optr++) = *(iptr ) << 24 | *(iptr + d->channels) << 16; | |
629 | *(optr++) = *(iptr+1) << 24 | *(iptr + d->channels + 1) << 16; | |
630 | iptr += bytes_per_frame; | |
631 | } | |
632 | } | |
633 | ||
634 | break; | |
635 | ||
636 | case DSD_U8: | |
637 | ||
638 | if (d->channels == 1) { | |
639 | while (count--) { | |
640 | *(optr++) = *(iptr) << 24; | |
641 | *(optr++) = *(iptr) << 24; | |
642 | iptr += bytes_per_frame; | |
643 | } | |
644 | } else { | |
645 | while (count--) { | |
646 | *(optr++) = *(iptr ) << 24; | |
647 | *(optr++) = *(iptr+1) << 24; | |
648 | iptr += bytes_per_frame; | |
649 | } | |
650 | } | |
651 | ||
652 | break; | |
653 | ||
654 | case DOP: | |
655 | case DOP_S24_LE: | |
656 | case DOP_S24_3LE: | |
423 | 657 | |
424 | 658 | if (d->channels == 1) { |
425 | 659 | while (count--) { |
435 | 669 | } |
436 | 670 | } |
437 | 671 | |
438 | } else { | |
672 | break; | |
673 | ||
674 | case PCM: | |
439 | 675 | |
440 | 676 | if (d->channels == 1) { |
441 | 677 | float *iptrf = d->transfer[0]; |
464 | 700 | } |
465 | 701 | } |
466 | 702 | |
703 | break; | |
704 | ||
467 | 705 | } |
468 | 706 | |
469 | 707 | _buf_inc_readp(streambuf, bytes_read); |
490 | 728 | |
491 | 729 | static decode_state dsd_decode(void) { |
492 | 730 | decode_state ret; |
731 | char *fmtstr; | |
732 | ||
733 | fmtstr = "None"; | |
493 | 734 | |
494 | 735 | LOCK_S; |
495 | 736 | |
526 | 767 | LOG_INFO("setting track_start"); |
527 | 768 | output.track_start = outputbuf->writep; |
528 | 769 | |
529 | dop = output.has_dop; | |
530 | ||
531 | if (dop && d->sample_rate / 16 > output.supported_rates[0]) { | |
532 | LOG_INFO("DOP sample rate too high for device - converting to PCM"); | |
533 | dop = false; | |
534 | } | |
535 | ||
536 | if (dop) { | |
537 | LOG_INFO("DOP output"); | |
538 | output.next_dop = true; | |
770 | outfmt = output.dsdfmt; | |
771 | ||
772 | switch (outfmt) { | |
773 | case DSD_U32_LE: | |
774 | fmtstr = "DSD_U32_LE"; | |
775 | output.next_sample_rate = d->sample_rate / 32; | |
776 | break; | |
777 | case DSD_U32_BE: | |
778 | fmtstr = "DSD_U32_BE"; | |
779 | output.next_sample_rate = d->sample_rate / 32; | |
780 | break; | |
781 | case DSD_U16_LE: | |
782 | fmtstr = "DSD_U16_LE"; | |
539 | 783 | output.next_sample_rate = d->sample_rate / 16; |
540 | output.fade = FADE_INACTIVE; | |
541 | } else { | |
784 | break; | |
785 | case DSD_U16_BE: | |
786 | fmtstr = "DSD_U16_BE"; | |
787 | output.next_sample_rate = d->sample_rate / 16; | |
788 | break; | |
789 | case DSD_U8: | |
790 | fmtstr = "DSD_U8"; | |
791 | output.next_sample_rate = d->sample_rate / 8; | |
792 | break; | |
793 | case DOP: | |
794 | fmtstr = "DOP"; | |
795 | output.next_sample_rate = d->sample_rate / 16; | |
796 | break; | |
797 | case DOP_S24_LE: | |
798 | fmtstr = "DOP_S24_LE"; | |
799 | output.next_sample_rate = d->sample_rate / 16; | |
800 | break; | |
801 | case DOP_S24_3LE: | |
802 | fmtstr = "DOP_S24_3LE"; | |
803 | output.next_sample_rate = d->sample_rate / 16; | |
804 | break; | |
805 | case PCM: | |
806 | // PCM case after DSD rate check and possible fallback to PCM conversion | |
807 | break; | |
808 | } | |
809 | ||
810 | if (outfmt != PCM && output.next_sample_rate > output.supported_rates[0]) { | |
811 | LOG_INFO("DSD sample rate too high for device - converting to PCM"); | |
812 | outfmt = PCM; | |
813 | } | |
814 | ||
815 | if (outfmt == PCM) { | |
542 | 816 | LOG_INFO("DSD to PCM output"); |
543 | output.next_dop = false; | |
544 | 817 | output.next_sample_rate = decode_newstream(d->sample_rate / 8, output.supported_rates); |
545 | 818 | if (output.fade_mode) _checkfade(true); |
546 | } | |
547 | ||
819 | } else { | |
820 | LOG_INFO("DSD%u stream, format: %s, rate: %uHz\n", d->sample_rate / 44100, fmtstr, output.next_sample_rate); | |
821 | output.fade = FADE_INACTIVE; | |
822 | } | |
823 | ||
824 | output.next_fmt = outfmt; | |
548 | 825 | decode.new_stream = false; |
549 | 826 | |
550 | 827 | UNLOCK_O; |
567 | 844 | UNLOCK_S; |
568 | 845 | |
569 | 846 | return ret; |
847 | } | |
848 | ||
849 | void dsd_init(dsd_format format, unsigned delay) { | |
850 | LOCK_O; | |
851 | output.dsdfmt = format; | |
852 | output.dsd_delay = delay; | |
853 | UNLOCK_O; | |
570 | 854 | } |
571 | 855 | |
572 | 856 | static void dsd_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { |
624 | 908 | return &ret; |
625 | 909 | } |
626 | 910 | |
911 | // invert polarity for frames in the output buffer | |
912 | void dsd_invert(u32_t *ptr, frames_t frames) { | |
913 | while (frames--) { | |
914 | *ptr = ~(*ptr); | |
915 | ++ptr; | |
916 | *ptr = ~(*ptr); | |
917 | ++ptr; | |
918 | } | |
919 | } | |
920 | ||
921 | // fill silence buffer with 10101100 which represents dsd silence | |
922 | void dsd_silence_frames(u32_t *ptr, frames_t frames) { | |
923 | while (frames--) { | |
924 | *ptr++ = 0x69696969; | |
925 | *ptr++ = 0x69696969; | |
926 | } | |
927 | } | |
928 | ||
627 | 929 | #endif // DSD |
1 | 1 | * Squeezelite - lightweight headless squeezebox emulator |
2 | 2 | * |
3 | 3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.com |
4 | * Ralph Irving 2015-2017, ralph_irving@hotmail.com | |
4 | 5 | * |
5 | 6 | * This program is free software: you can redistribute it and/or modify |
6 | 7 | * it under the terms of the GNU General Public License as published by |
20 | 21 | #include "squeezelite.h" |
21 | 22 | |
22 | 23 | #include <neaacdec.h> |
24 | ||
25 | #if BYTES_PER_FRAME == 4 | |
26 | #define ALIGN(n) (n) | |
27 | #else | |
28 | #define ALIGN(n) (n << 8) | |
29 | #endif | |
23 | 30 | |
24 | 31 | #define WRAPBUF_LEN 2048 |
25 | 32 | |
301 | 308 | a->pos += bytes; |
302 | 309 | a->consume = consume - bytes; |
303 | 310 | break; |
311 | } else if (len > streambuf->size) { | |
312 | // can't process an atom larger than streambuf! | |
313 | LOG_ERROR("atom %s too large for buffer %u %u", type, len, streambuf->size); | |
314 | return -1; | |
304 | 315 | } else { |
316 | // make sure there is 'len' contiguous space | |
317 | _buf_unwrap(streambuf, len); | |
305 | 318 | break; |
306 | 319 | } |
307 | 320 | } |
312 | 325 | static decode_state faad_decode(void) { |
313 | 326 | size_t bytes_total; |
314 | 327 | size_t bytes_wrap; |
315 | NeAACDecFrameInfo info; | |
316 | s32_t *iptr; | |
328 | static NeAACDecFrameInfo info; | |
329 | ISAMPLE_T *iptr; | |
317 | 330 | bool endstream; |
318 | 331 | frames_t frames; |
319 | 332 | |
375 | 388 | LOCK_O; |
376 | 389 | LOG_INFO("setting track_start"); |
377 | 390 | output.next_sample_rate = decode_newstream(samplerate, output.supported_rates); |
378 | IF_DSD( output.next_dop = false; ) | |
391 | IF_DSD( output.next_fmt = PCM; ) | |
379 | 392 | output.track_start = outputbuf->writep; |
380 | 393 | if (output.fade_mode) _checkfade(true); |
381 | 394 | decode.new_stream = false; |
398 | 411 | if (bytes_wrap < WRAPBUF_LEN && bytes_total > WRAPBUF_LEN) { |
399 | 412 | |
400 | 413 | // make a local copy of frames which may have wrapped round the end of streambuf |
401 | u8_t buf[WRAPBUF_LEN]; | |
414 | static u8_t buf[WRAPBUF_LEN]; | |
402 | 415 | memcpy(buf, streambuf->readp, bytes_wrap); |
403 | 416 | memcpy(buf + bytes_wrap, streambuf->buf, WRAPBUF_LEN - bytes_wrap); |
404 | 417 | |
489 | 502 | while (frames > 0) { |
490 | 503 | frames_t f; |
491 | 504 | frames_t count; |
492 | s32_t *optr; | |
505 | ISAMPLE_T *optr; | |
493 | 506 | |
494 | 507 | IF_DIRECT( |
495 | 508 | f = _buf_cont_write(outputbuf) / BYTES_PER_FRAME; |
496 | optr = (s32_t *)outputbuf->writep; | |
509 | optr = (ISAMPLE_T *)outputbuf->writep; | |
497 | 510 | ); |
498 | 511 | IF_PROCESS( |
499 | 512 | f = process.max_in_frames; |
500 | optr = (s32_t *)process.inbuf; | |
513 | optr = (ISAMPLE_T *)process.inbuf; | |
501 | 514 | ); |
502 | 515 | |
503 | 516 | f = min(f, frames); |
504 | 517 | count = f; |
505 | 518 | |
506 | 519 | if (info.channels == 2) { |
520 | #if BYTES_PER_FRAME == 4 | |
521 | memcpy(optr, iptr, count * BYTES_PER_FRAME); | |
522 | iptr += count * 2; | |
523 | #else | |
507 | 524 | while (count--) { |
508 | *optr++ = *iptr++ << 8; | |
509 | *optr++ = *iptr++ << 8; | |
510 | } | |
525 | *optr++ = ALIGN(*iptr++); | |
526 | *optr++ = ALIGN(*iptr++); | |
527 | } | |
528 | #endif | |
511 | 529 | } else if (info.channels == 1) { |
512 | 530 | while (count--) { |
513 | *optr++ = *iptr << 8; | |
514 | *optr++ = *iptr++ << 8; | |
531 | *optr++ = ALIGN(*iptr); | |
532 | *optr++ = ALIGN(*iptr++); | |
515 | 533 | } |
516 | 534 | } else { |
517 | 535 | LOG_WARN("unsupported number of channels"); |
561 | 579 | |
562 | 580 | conf = NEAAC(a, GetCurrentConfiguration, a->hAac); |
563 | 581 | |
582 | #if BYTES_PER_FRAME == 4 | |
583 | conf->outputFormat = FAAD_FMT_16BIT; | |
584 | #else | |
564 | 585 | conf->outputFormat = FAAD_FMT_24BIT; |
586 | #endif | |
587 | conf->defSampleRate = 44100; | |
565 | 588 | conf->downMatrix = 1; |
566 | 589 | |
567 | 590 | if (!NEAAC(a, SetConfiguration, a->hAac, conf)) { |
1 | 1 | * Squeezelite - lightweight headless squeezebox emulator |
2 | 2 | * |
3 | 3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.com |
4 | * Ralph Irving 2015-2017, ralph_irving@hotmail.com | |
4 | 5 | * |
5 | 6 | * This program is free software: you can redistribute it and/or modify |
6 | 7 | * it under the terms of the GNU General Public License as published by |
41 | 42 | AVInputFormat *input_format; |
42 | 43 | AVFormatContext *formatC; |
43 | 44 | AVCodecContext *codecC; |
45 | AVCodecParameters *codecP; | |
44 | 46 | AVFrame *frame; |
45 | 47 | AVPacket *avpkt; |
46 | 48 | unsigned mmsh_bytes_left; |
51 | 53 | unsigned (* avcodec_version)(void); |
52 | 54 | AVCodec * (* avcodec_find_decoder)(int); |
53 | 55 | int attribute_align_arg (* avcodec_open2)(AVCodecContext *, const AVCodec *, AVDictionary **); |
56 | #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1) | |
57 | AVFrame * (* av_frame_alloc)(void); | |
58 | void (* av_frame_free)(AVFrame **); | |
59 | #else | |
54 | 60 | AVFrame * (* avcodec_alloc_frame)(void); |
55 | 61 | void (* avcodec_free_frame)(AVFrame **); |
62 | #endif | |
56 | 63 | int attribute_align_arg (* avcodec_decode_audio4)(AVCodecContext *, AVFrame *, int *, const AVPacket *); |
64 | AVCodecContext * (* avcodec_alloc_context3)(const AVCodec *); | |
65 | void (* avcodec_free_context)(AVCodecContext **); | |
66 | int (* avcodec_parameters_to_context)(AVCodecContext *, const AVCodecParameters *); | |
57 | 67 | // ffmpeg symbols to be dynamically loaded from libavformat |
58 | 68 | unsigned (* avformat_version)(void); |
59 | 69 | AVFormatContext * (* avformat_alloc_context)(void); |
63 | 73 | AVIOContext * (* avio_alloc_context)(unsigned char *, int, int, void *, |
64 | 74 | int (*read_packet)(void *, uint8_t *, int), int (*write_packet)(void *, uint8_t *, int), int64_t (*seek)(void *, int64_t, int)); |
65 | 75 | void (* av_init_packet)(AVPacket *); |
76 | #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,102) | |
77 | void (* av_packet_unref)(AVPacket *); | |
78 | #else | |
66 | 79 | void (* av_free_packet)(AVPacket *); |
80 | #endif | |
67 | 81 | int (* av_read_frame)(AVFormatContext *, AVPacket *); |
68 | 82 | AVInputFormat * (* av_find_input_format)(const char *); |
69 | 83 | void (* av_register_all)(void); |
118 | 132 | |
119 | 133 | |
120 | 134 | // our own version of useful error function not included in earlier ffmpeg versions |
121 | static char *av__err2str(errnum) { | |
135 | static char *av__err2str(int errnum) { | |
122 | 136 | static char buf[64]; |
123 | 137 | AV(ff, strerror, errnum, buf, 64); |
124 | 138 | return buf; |
156 | 170 | } |
157 | 171 | |
158 | 172 | static int _read_data(void *opaque, u8_t *buffer, int buf_size) { |
159 | size_t bytes; | |
173 | unsigned int bytes; | |
160 | 174 | |
161 | 175 | LOCK_S; |
162 | 176 | |
263 | 277 | ff->mmsh_bytes_left = ff->mmsh_bytes_pad = ff->mmsh_packet_len = 0; |
264 | 278 | |
265 | 279 | if (!ff->readbuf) { |
266 | ff->readbuf = AV(ff, malloc, READ_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); | |
280 | ff->readbuf = AV(ff, malloc, READ_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); | |
267 | 281 | } |
268 | 282 | |
269 | 283 | avio = AVIO(ff, alloc_context, ff->readbuf, READ_SIZE, 0, NULL, _read_data, NULL, NULL); |
293 | 307 | } |
294 | 308 | |
295 | 309 | if (ff->wma && ff->wma_playstream < ff->formatC->nb_streams) { |
296 | if (ff->formatC->streams[ff->wma_playstream]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { | |
310 | if (ff->formatC->streams[ff->wma_playstream]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { | |
297 | 311 | LOG_INFO("using wma stream sent from server: %i", ff->wma_playstream); |
298 | 312 | audio_stream = ff->wma_playstream; |
299 | 313 | } |
300 | 314 | } |
301 | 315 | |
302 | 316 | if (audio_stream == -1) { |
303 | int i; | |
317 | unsigned int i; | |
304 | 318 | for (i = 0; i < ff->formatC->nb_streams; ++i) { |
305 | if (ff->formatC->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { | |
319 | if (ff->formatC->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { | |
306 | 320 | audio_stream = i; |
307 | 321 | LOG_INFO("found stream: %i", i); |
308 | 322 | break; |
317 | 331 | |
318 | 332 | av_stream = ff->formatC->streams[audio_stream]; |
319 | 333 | |
320 | ff->codecC = av_stream->codec; | |
321 | ||
322 | codec = AVCODEC(ff, find_decoder, ff->codecC->codec_id); | |
334 | ff->codecC = AVCODEC (ff, alloc_context3, NULL); | |
335 | if ( ff->codecC == NULL ) { | |
336 | LOG_ERROR("can't allocate avctx"); | |
337 | return DECODE_ERROR; | |
338 | } | |
339 | ||
340 | ff->codecP = av_stream->codecpar; | |
341 | ||
342 | if ( (AVCODEC(ff, parameters_to_context, ff->codecC, ff->codecP) ) < 0) { | |
343 | AVCODEC(ff, free_context, &ff->codecC); | |
344 | ||
345 | LOG_ERROR("can't initialize avctx"); | |
346 | return DECODE_ERROR; | |
347 | } | |
348 | ||
349 | codec = AVCODEC(ff, find_decoder, ff->codecP->codec_id); | |
323 | 350 | |
324 | 351 | AVCODEC(ff, open2, ff->codecC, codec, NULL); |
325 | 352 | |
353 | #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1) | |
354 | ff->frame = AV(ff, frame_alloc); | |
355 | #else | |
326 | 356 | ff->frame = AVCODEC(ff, alloc_frame); |
357 | #endif | |
327 | 358 | |
328 | 359 | ff->avpkt = AV(ff, malloc, sizeof(AVPacket)); |
329 | 360 | if (ff->avpkt == NULL) { |
337 | 368 | |
338 | 369 | LOCK_O; |
339 | 370 | LOG_INFO("setting track_start"); |
340 | output.next_sample_rate = decode_newstream(ff->codecC->sample_rate, output.supported_rates); | |
341 | IF_DSD( output.next_dop = false; ) | |
371 | output.next_sample_rate = decode_newstream(ff->codecP->sample_rate, output.supported_rates); | |
372 | IF_DSD( output.next_fmt = PCM; ) | |
342 | 373 | output.track_start = outputbuf->writep; |
343 | 374 | if (output.fade_mode) _checkfade(true); |
344 | 375 | decode.new_stream = false; |
374 | 405 | len = AVCODEC(ff, decode_audio4, ff->codecC, ff->frame, &got_frame, &pkt_c); |
375 | 406 | if (len < 0) { |
376 | 407 | LOG_ERROR("avcodec_decode_audio4 error: %i %s", len, av__err2str(len)); |
377 | return DECODE_RUNNING; | |
408 | break; // exit loop, free the packet, and continue decoding | |
378 | 409 | } |
379 | 410 | |
380 | 411 | pkt_c.data += len; |
504 | 535 | } |
505 | 536 | } |
506 | 537 | |
538 | #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,102) | |
539 | AV(ff, packet_unref, ff->avpkt); | |
540 | #else | |
507 | 541 | AV(ff, free_packet, ff->avpkt); |
542 | #endif | |
508 | 543 | |
509 | 544 | return DECODE_RUNNING; |
510 | 545 | } |
511 | 546 | |
512 | 547 | static void _free_ff_data(void) { |
548 | if (ff->codecC) { | |
549 | AVCODEC(ff, free_context, &ff->codecC); | |
550 | ff->codecC = NULL; | |
551 | } | |
552 | ||
513 | 553 | if (ff->formatC) { |
514 | if (ff->formatC->pb) AV(ff, freep, &ff->formatC->pb); | |
554 | if (ff->formatC->pb) { | |
555 | // 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 | |
556 | // otherwise, a double free can occur (seen by valgrind), resulting in e.g. SIGILL | |
557 | AV(ff, freep, &ff->formatC->pb->buffer); | |
558 | ff->readbuf = NULL; | |
559 | AV(ff, freep, &ff->formatC->pb); | |
560 | } | |
561 | ||
515 | 562 | AVFORMAT(ff, free_context, ff->formatC); |
516 | 563 | ff->formatC = NULL; |
517 | 564 | } |
519 | 566 | if (ff->frame) { |
520 | 567 | // ffmpeg version dependant free function |
521 | 568 | #if !LINKALL |
569 | #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1) | |
570 | ff->av_frame_free ? AV(ff, frame_free, &ff->frame) : AV(ff, freep, &ff->frame); | |
571 | #else | |
522 | 572 | ff->avcodec_free_frame ? AVCODEC(ff, free_frame, &ff->frame) : AV(ff, freep, &ff->frame); |
573 | #endif | |
523 | 574 | #elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,28,0) |
575 | #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1) | |
576 | AV(ff, frame_free, &ff->frame); | |
577 | #else | |
524 | 578 | AVCODEC(ff, free_frame, &ff->frame); |
579 | #endif | |
525 | 580 | #else |
526 | 581 | AV(ff, freep, &ff->frame); |
527 | 582 | #endif |
529 | 584 | } |
530 | 585 | |
531 | 586 | if (ff->avpkt) { |
587 | #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,102) | |
588 | AV(ff, packet_unref, ff->avpkt); | |
589 | #else | |
532 | 590 | AV(ff, free_packet, ff->avpkt); |
591 | #endif | |
533 | 592 | AV(ff, freep, &ff->avpkt); |
534 | 593 | ff->avpkt = NULL; |
535 | 594 | } |
567 | 626 | |
568 | 627 | static void ff_close(void) { |
569 | 628 | _free_ff_data(); |
570 | ||
571 | if (ff->readbuf) { | |
572 | AV(ff, freep, &ff->readbuf); | |
573 | ff->readbuf = NULL; | |
574 | } | |
575 | 629 | } |
576 | 630 | |
577 | 631 | static bool load_ff() { |
606 | 660 | ff->avcodec_version = dlsym(handle_codec, "avcodec_version"); |
607 | 661 | ff->avcodec_find_decoder = dlsym(handle_codec, "avcodec_find_decoder"); |
608 | 662 | ff->avcodec_open2 = dlsym(handle_codec, "avcodec_open2"); |
663 | #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55,28,1) | |
664 | ff->av_frame_alloc = dlsym(handle_codec, "av_frame_alloc"); | |
665 | ff->av_frame_free = dlsym(handle_codec, "av_frame_free"); | |
666 | #else | |
609 | 667 | ff->avcodec_alloc_frame = dlsym(handle_codec, "avcodec_alloc_frame"); |
610 | 668 | ff->avcodec_free_frame = dlsym(handle_codec, "avcodec_free_frame"); |
669 | #endif | |
611 | 670 | ff->avcodec_decode_audio4 = dlsym(handle_codec, "avcodec_decode_audio4"); |
671 | ff->avcodec_alloc_context3 = dlsym(handle_format, "avcodec_alloc_context3"); | |
672 | ff->avcodec_free_context = dlsym(handle_format, "avcodec_free_context"); | |
673 | ff->avcodec_parameters_to_context = dlsym(handle_format, "avcodec_parameters_to_context"); | |
612 | 674 | ff->av_init_packet = dlsym(handle_codec, "av_init_packet"); |
675 | #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57,24,102) | |
676 | ff->av_packet_unref = dlsym(handle_codec, "av_packet_unref"); | |
677 | #else | |
613 | 678 | ff->av_free_packet = dlsym(handle_codec, "av_free_packet"); |
679 | #endif | |
614 | 680 | |
615 | 681 | if ((err = dlerror()) != NULL) { |
616 | 682 | LOG_INFO("dlerror: %s", err); |
703 | 769 | |
704 | 770 | static struct codec ret = { |
705 | 771 | 'w', // id |
706 | "wma,wmap,wmal", // types | |
772 | "wma,wmap", // types | |
707 | 773 | READ_SIZE, // min read |
708 | 774 | WRITE_SIZE, // min space |
709 | 775 | ff_open_wma, // open |
711 | 777 | ff_decode, // decode |
712 | 778 | }; |
713 | 779 | |
714 | LOG_INFO("using ffmpeg to decode wma,wmap,wmal"); | |
780 | LOG_INFO("using ffmpeg to decode wma,wmap"); | |
715 | 781 | return &ret; |
716 | 782 | } |
717 | 783 |
21 | 21 | |
22 | 22 | #include <FLAC/stream_decoder.h> |
23 | 23 | |
24 | #if BYTES_PER_FRAME == 4 | |
25 | #define ALIGN8(n) (n << 8) | |
26 | #define ALIGN16(n) (n) | |
27 | #define ALIGN24(n) (n >> 8) | |
28 | #define ALIGN32(n) (n >> 16) | |
29 | #else | |
30 | #define ALIGN8(n) (n << 24) | |
31 | #define ALIGN16(n) (n << 16) | |
32 | #define ALIGN24(n) (n << 8) | |
33 | #define ALIGN32(n) (n) | |
34 | #endif | |
35 | ||
24 | 36 | struct flac { |
25 | 37 | FLAC__StreamDecoder *decoder; |
38 | u8_t container; | |
26 | 39 | #if !LINKALL |
27 | 40 | // FLAC symbols to be dynamically loaded |
28 | 41 | const char **FLAC__StreamDecoderErrorStatusString; |
42 | 55 | FLAC__StreamDecoderErrorCallback error_callback, |
43 | 56 | void *client_data |
44 | 57 | ); |
58 | FLAC__StreamDecoderInitStatus (* FLAC__stream_decoder_init_ogg_stream)( | |
59 | FLAC__StreamDecoder *decoder, | |
60 | FLAC__StreamDecoderReadCallback read_callback, | |
61 | FLAC__StreamDecoderSeekCallback seek_callback, | |
62 | FLAC__StreamDecoderTellCallback tell_callback, | |
63 | FLAC__StreamDecoderLengthCallback length_callback, | |
64 | FLAC__StreamDecoderEofCallback eof_callback, | |
65 | FLAC__StreamDecoderWriteCallback write_callback, | |
66 | FLAC__StreamDecoderMetadataCallback metadata_callback, | |
67 | FLAC__StreamDecoderErrorCallback error_callback, | |
68 | void *client_data | |
69 | ); | |
45 | 70 | FLAC__bool (* FLAC__stream_decoder_process_single)(FLAC__StreamDecoder *decoder); |
46 | 71 | FLAC__StreamDecoderState (* FLAC__stream_decoder_get_state)(const FLAC__StreamDecoder *decoder); |
47 | 72 | #endif |
117 | 142 | decode.new_stream = false; |
118 | 143 | |
119 | 144 | #if DSD |
120 | if (output.has_dop && bits_per_sample == 24 && is_flac_dop((u32_t *)lptr, (u32_t *)rptr, frames)) { | |
145 | #if SL_LITTLE_ENDIAN | |
146 | #define MARKER_OFFSET 2 | |
147 | #else | |
148 | #define MARKER_OFFSET 1 | |
149 | #endif | |
150 | if (bits_per_sample == 24 && is_stream_dop(((u8_t *)lptr) + MARKER_OFFSET, ((u8_t *)rptr) + MARKER_OFFSET, 4, frames)) { | |
121 | 151 | LOG_INFO("file contains DOP"); |
122 | output.next_dop = true; | |
152 | if (output.dsdfmt == DOP_S24_LE || output.dsdfmt == DOP_S24_3LE) | |
153 | output.next_fmt = output.dsdfmt; | |
154 | else | |
155 | output.next_fmt = DOP; | |
123 | 156 | output.next_sample_rate = frame->header.sample_rate; |
124 | 157 | output.fade = FADE_INACTIVE; |
125 | 158 | } else { |
126 | 159 | output.next_sample_rate = decode_newstream(frame->header.sample_rate, output.supported_rates); |
127 | output.next_dop = false; | |
160 | output.next_fmt = PCM; | |
128 | 161 | if (output.fade_mode) _checkfade(true); |
129 | 162 | } |
130 | 163 | #else |
140 | 173 | while (frames > 0) { |
141 | 174 | frames_t f; |
142 | 175 | frames_t count; |
143 | s32_t *optr; | |
176 | ISAMPLE_T *optr; | |
144 | 177 | |
145 | 178 | IF_DIRECT( |
146 | optr = (s32_t *)outputbuf->writep; | |
179 | optr = (ISAMPLE_T *)outputbuf->writep; | |
147 | 180 | f = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; |
148 | 181 | ); |
149 | 182 | IF_PROCESS( |
150 | optr = (s32_t *)process.inbuf; | |
183 | optr = (ISAMPLE_T *)process.inbuf; | |
151 | 184 | f = process.max_in_frames; |
152 | 185 | ); |
153 | 186 | |
157 | 190 | |
158 | 191 | if (bits_per_sample == 8) { |
159 | 192 | while (count--) { |
160 | *optr++ = *lptr++ << 24; | |
161 | *optr++ = *rptr++ << 24; | |
193 | *optr++ = ALIGN8(*lptr++); | |
194 | *optr++ = ALIGN8(*rptr++); | |
162 | 195 | } |
163 | 196 | } else if (bits_per_sample == 16) { |
164 | 197 | while (count--) { |
165 | *optr++ = *lptr++ << 16; | |
166 | *optr++ = *rptr++ << 16; | |
198 | *optr++ = ALIGN16(*lptr++); | |
199 | *optr++ = ALIGN16(*rptr++); | |
167 | 200 | } |
168 | 201 | } else if (bits_per_sample == 24) { |
169 | 202 | while (count--) { |
170 | *optr++ = *lptr++ << 8; | |
171 | *optr++ = *rptr++ << 8; | |
203 | *optr++ = ALIGN24(*lptr++); | |
204 | *optr++ = ALIGN24(*rptr++); | |
172 | 205 | } |
173 | 206 | } else if (bits_per_sample == 32) { |
174 | 207 | while (count--) { |