New upstream version 1.8
tony mancill
3 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 | Squeezelite - lightweight headless squeezebox emulator | |
1 | ||
2 | (c) Adrian Smith 2012-2015, triode1@btinternet.com | |
3 | ||
4 | Released under GPLv3 license: | |
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 | ||
22 | If built with DSD support, this software also includes code subject to the following license: | |
23 | ||
24 | Copyright 2009, 2011 Sebastian Gesemann. All rights reserved. | |
25 | ||
26 | Redistribution and use in source and binary forms, with or without modification, are | |
27 | permitted provided that the following conditions are met: | |
28 | ||
29 | 1. Redistributions of source code must retain the above copyright notice, this list of | |
30 | conditions and the following disclaimer. | |
31 | ||
32 | 2. Redistributions in binary form must reproduce the above copyright notice, this list | |
33 | of conditions and the following disclaimer in the documentation and/or other materials | |
34 | provided with the distribution. | |
35 | ||
36 | THIS SOFTWARE IS PROVIDED BY SEBASTIAN GESEMANN ''AS IS'' AND ANY EXPRESS OR IMPLIED | |
37 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
38 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN GESEMANN OR | |
39 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
40 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
41 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
42 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
43 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
44 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
45 | ||
46 | The views and conclusions contained in the software and documentation are those of the | |
47 | authors and should not be interpreted as representing official policies, either expressed | |
48 | or implied, of Sebastian Gesemann. |
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 | |
3 | EXECUTABLE ?= squeezelite | |
4 | ||
5 | # passing one or more of these in $(OPTS) enables optional feature inclusion | |
6 | OPT_DSD = -DDSD | |
7 | OPT_FF = -DFFMPEG | |
8 | OPT_LINKALL = -DLINKALL | |
9 | OPT_RESAMPLE= -DRESAMPLE | |
10 | OPT_VIS = -DVISEXPORT | |
11 | OPT_IR = -DIR | |
12 | ||
13 | SOURCES = \ | |
14 | 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 | |
17 | ||
18 | SOURCES_DSD = dsd.c dop.c dsd2pcm/dsd2pcm.c | |
19 | SOURCES_FF = ffmpeg.c | |
20 | SOURCES_RESAMPLE = process.c resample.c | |
21 | SOURCES_VIS = output_vis.c | |
22 | SOURCES_IR = ir.c | |
23 | ||
24 | LINK_LINUX = -ldl | |
25 | ||
26 | LINKALL = -lFLAC -lmad -lvorbisfile -lfaad -lmpg123 | |
27 | LINKALL_FF = -lavcodec -lavformat -lavutil | |
28 | LINKALL_RESAMPLE = -lsoxr | |
29 | LINKALL_IR = -llirc_client | |
30 | ||
31 | DEPS = squeezelite.h slimproto.h | |
32 | ||
33 | UNAME = $(shell uname -s) | |
34 | ||
35 | # add optional sources | |
36 | ifneq (,$(findstring $(OPT_DSD), $(CFLAGS))) | |
37 | SOURCES += $(SOURCES_DSD) | |
38 | endif | |
39 | ifneq (,$(findstring $(OPT_FF), $(CFLAGS))) | |
40 | SOURCES += $(SOURCES_FF) | |
41 | endif | |
42 | ifneq (,$(findstring $(OPT_RESAMPLE), $(CFLAGS))) | |
43 | SOURCES += $(SOURCES_RESAMPLE) | |
44 | endif | |
45 | ifneq (,$(findstring $(OPT_VIS), $(CFLAGS))) | |
46 | SOURCES += $(SOURCES_VIS) | |
47 | endif | |
48 | ifneq (,$(findstring $(OPT_IR), $(CFLAGS))) | |
49 | SOURCES += $(SOURCES_IR) | |
50 | endif | |
51 | ||
52 | # add optional link options | |
53 | ifneq (,$(findstring $(OPT_LINKALL), $(CFLAGS))) | |
54 | LDFLAGS += $(LINKALL) | |
55 | ifneq (,$(findstring $(OPT_FF), $(CFLAGS))) | |
56 | LDFLAGS += $(LINKALL_FF) | |
57 | endif | |
58 | ifneq (,$(findstring $(OPT_RESAMPLE), $(CFLAGS))) | |
59 | LDFLAGS += $(LINKALL_RESAMPLE) | |
60 | endif | |
61 | ifneq (,$(findstring $(OPT_IR), $(CFLAGS))) | |
62 | LDFLAGS += $(LINKALL_IR) | |
63 | endif | |
64 | else | |
65 | # if not LINKALL and linux add LINK_LINUX | |
66 | ifeq ($(UNAME), Linux) | |
67 | LDFLAGS += $(LINK_LINUX) | |
68 | endif | |
69 | endif | |
70 | ||
71 | OBJECTS = $(SOURCES:.c=.o) | |
72 | ||
73 | all: $(EXECUTABLE) | |
74 | ||
75 | $(EXECUTABLE): $(OBJECTS) | |
76 | $(CC) $(OBJECTS) $(LDFLAGS) -o $@ | |
77 | ||
78 | $(OBJECTS): $(DEPS) | |
79 | ||
80 | .c.o: | |
81 | $(CC) $(CFLAGS) $(CPPFLAGS) $< -c -o $@ | |
82 | ||
83 | clean: | |
84 | rm -f $(OBJECTS) $(EXECUTABLE) |
0 | 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 | ||
3 | include Makefile |
0 | # OSX build - adjust -I to point to header files for codecs and portaudio | |
1 | CFLAGS = -arch x86_64 -arch i386 -Wall -fPIC -O2 -I./include $(OPTS) | |
2 | LDFLAGS = -arch x86_64 -arch i386 -lpthread libportaudio.a -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon | |
3 | ||
4 | EXECUTABLE ?= squeezelite-osx | |
5 | ||
6 | include Makefile |
0 | # Make with portaudio rather than direct alsa | |
1 | OPTS += -DPORTAUDIO | |
2 | LDFLAGS = -lportaudio -lpthread -ldl -lrt | |
3 | EXECUTABLE = squeezelite-pa | |
4 | ||
5 | include Makefile |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // fifo bufffers | |
21 | ||
22 | #define _GNU_SOURCE | |
23 | ||
24 | #include "squeezelite.h" | |
25 | ||
26 | // _* called with muxtex locked | |
27 | ||
28 | inline unsigned _buf_used(struct buffer *buf) { | |
29 | return buf->writep >= buf->readp ? buf->writep - buf->readp : buf->size - (buf->readp - buf->writep); | |
30 | } | |
31 | ||
32 | unsigned _buf_space(struct buffer *buf) { | |
33 | return buf->size - _buf_used(buf) - 1; // reduce by one as full same as empty otherwise | |
34 | } | |
35 | ||
36 | unsigned _buf_cont_read(struct buffer *buf) { | |
37 | return buf->writep >= buf->readp ? buf->writep - buf->readp : buf->wrap - buf->readp; | |
38 | } | |
39 | ||
40 | unsigned _buf_cont_write(struct buffer *buf) { | |
41 | return buf->writep >= buf->readp ? buf->wrap - buf->writep : buf->readp - buf->writep; | |
42 | } | |
43 | ||
44 | void _buf_inc_readp(struct buffer *buf, unsigned by) { | |
45 | buf->readp += by; | |
46 | if (buf->readp >= buf->wrap) { | |
47 | buf->readp -= buf->size; | |
48 | } | |
49 | } | |
50 | ||
51 | void _buf_inc_writep(struct buffer *buf, unsigned by) { | |
52 | buf->writep += by; | |
53 | if (buf->writep >= buf->wrap) { | |
54 | buf->writep -= buf->size; | |
55 | } | |
56 | } | |
57 | ||
58 | void buf_flush(struct buffer *buf) { | |
59 | mutex_lock(buf->mutex); | |
60 | buf->readp = buf->buf; | |
61 | buf->writep = buf->buf; | |
62 | mutex_unlock(buf->mutex); | |
63 | } | |
64 | ||
65 | // adjust buffer to multiple of mod bytes so reading in multiple always wraps on frame boundary | |
66 | void buf_adjust(struct buffer *buf, size_t mod) { | |
67 | size_t size; | |
68 | mutex_lock(buf->mutex); | |
69 | size = ((unsigned)(buf->base_size / mod)) * mod; | |
70 | buf->readp = buf->buf; | |
71 | buf->writep = buf->buf; | |
72 | buf->wrap = buf->buf + size; | |
73 | buf->size = size; | |
74 | mutex_unlock(buf->mutex); | |
75 | } | |
76 | ||
77 | // called with mutex locked to resize, does not retain contents, reverts to original size if fails | |
78 | void _buf_resize(struct buffer *buf, size_t size) { | |
79 | free(buf->buf); | |
80 | buf->buf = malloc(size); | |
81 | if (!buf->buf) { | |
82 | size = buf->size; | |
83 | buf->buf= malloc(size); | |
84 | if (!buf->buf) { | |
85 | size = 0; | |
86 | } | |
87 | } | |
88 | buf->readp = buf->buf; | |
89 | buf->writep = buf->buf; | |
90 | buf->wrap = buf->buf + size; | |
91 | buf->size = size; | |
92 | buf->base_size = size; | |
93 | } | |
94 | ||
95 | void buf_init(struct buffer *buf, size_t size) { | |
96 | buf->buf = malloc(size); | |
97 | buf->readp = buf->buf; | |
98 | buf->writep = buf->buf; | |
99 | buf->wrap = buf->buf + size; | |
100 | buf->size = size; | |
101 | buf->base_size = size; | |
102 | mutex_create_p(buf->mutex); | |
103 | } | |
104 | ||
105 | void buf_destroy(struct buffer *buf) { | |
106 | if (buf->buf) { | |
107 | free(buf->buf); | |
108 | buf->buf = NULL; | |
109 | buf->size = 0; | |
110 | buf->base_size = 0; | |
111 | mutex_destroy(buf->mutex); | |
112 | } | |
113 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // decode thread | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | log_level loglevel; | |
25 | ||
26 | extern struct buffer *streambuf; | |
27 | extern struct buffer *outputbuf; | |
28 | extern struct streamstate stream; | |
29 | extern struct outputstate output; | |
30 | extern struct processstate process; | |
31 | ||
32 | struct decodestate decode; | |
33 | struct codec *codecs[MAX_CODECS]; | |
34 | struct codec *codec; | |
35 | static bool running = true; | |
36 | ||
37 | #define LOCK_S mutex_lock(streambuf->mutex) | |
38 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
39 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
40 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
41 | #define LOCK_D mutex_lock(decode.mutex); | |
42 | #define UNLOCK_D mutex_unlock(decode.mutex); | |
43 | ||
44 | #if PROCESS | |
45 | #define IF_DIRECT(x) if (decode.direct) { x } | |
46 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
47 | #define MAY_PROCESS(x) { x } | |
48 | #else | |
49 | #define IF_DIRECT(x) { x } | |
50 | #define IF_PROCESS(x) | |
51 | #define MAY_PROCESS(x) | |
52 | #endif | |
53 | ||
54 | static void *decode_thread() { | |
55 | ||
56 | while (running) { | |
57 | size_t bytes, space, min_space; | |
58 | bool toend; | |
59 | bool ran = false; | |
60 | ||
61 | LOCK_S; | |
62 | bytes = _buf_used(streambuf); | |
63 | toend = (stream.state <= DISCONNECT); | |
64 | UNLOCK_S; | |
65 | LOCK_O; | |
66 | space = _buf_space(outputbuf); | |
67 | UNLOCK_O; | |
68 | ||
69 | LOCK_D; | |
70 | ||
71 | if (decode.state == DECODE_RUNNING && codec) { | |
72 | ||
73 | LOG_SDEBUG("streambuf bytes: %u outputbuf space: %u", bytes, space); | |
74 | ||
75 | IF_DIRECT( | |
76 | min_space = codec->min_space; | |
77 | ); | |
78 | IF_PROCESS( | |
79 | min_space = process.max_out_frames * BYTES_PER_FRAME; | |
80 | ); | |
81 | ||
82 | if (space > min_space && (bytes > codec->min_read_bytes || toend)) { | |
83 | ||
84 | decode.state = codec->decode(); | |
85 | ||
86 | IF_PROCESS( | |
87 | if (process.in_frames) { | |
88 | process_samples(); | |
89 | } | |
90 | ||
91 | if (decode.state == DECODE_COMPLETE) { | |
92 | process_drain(); | |
93 | } | |
94 | ); | |
95 | ||
96 | if (decode.state != DECODE_RUNNING) { | |
97 | ||
98 | LOG_INFO("decode %s", decode.state == DECODE_COMPLETE ? "complete" : "error"); | |
99 | ||
100 | LOCK_O; | |
101 | if (output.fade_mode) _checkfade(false); | |
102 | UNLOCK_O; | |
103 | ||
104 | wake_controller(); | |
105 | } | |
106 | ||
107 | ran = true; | |
108 | } | |
109 | } | |
110 | ||
111 | UNLOCK_D; | |
112 | ||
113 | if (!ran) { | |
114 | usleep(100000); | |
115 | } | |
116 | } | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
121 | static thread_type thread; | |
122 | ||
123 | void decode_init(log_level level, const char *include_codecs, const char *exclude_codecs) { | |
124 | int i; | |
125 | ||
126 | loglevel = level; | |
127 | ||
128 | LOG_INFO("init decode, include codecs: %s exclude codecs: %s", include_codecs ? include_codecs : "", exclude_codecs); | |
129 | ||
130 | // register codecs | |
131 | // dsf,dff,alc,wma,wmap,wmal,aac,spt,ogg,ogf,flc,aif,pcm,mp3 | |
132 | i = 0; | |
133 | #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(); | |
144 | ||
145 | // try mad then mpg for mp3 unless command line option passed | |
146 | 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(); | |
150 | ||
151 | mutex_create(decode.mutex); | |
152 | ||
153 | #if LINUX || OSX || FREEBSD | |
154 | pthread_attr_t attr; | |
155 | pthread_attr_init(&attr); | |
156 | pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + DECODE_THREAD_STACK_SIZE); | |
157 | pthread_create(&thread, &attr, decode_thread, NULL); | |
158 | pthread_attr_destroy(&attr); | |
159 | #endif | |
160 | #if WIN | |
161 | thread = CreateThread(NULL, DECODE_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&decode_thread, NULL, 0, NULL); | |
162 | #endif | |
163 | ||
164 | decode.new_stream = true; | |
165 | decode.state = DECODE_STOPPED; | |
166 | ||
167 | MAY_PROCESS( | |
168 | decode.direct = true; | |
169 | decode.process = false; | |
170 | ); | |
171 | } | |
172 | ||
173 | void decode_close(void) { | |
174 | LOG_INFO("close decode"); | |
175 | LOCK_D; | |
176 | if (codec) { | |
177 | codec->close(); | |
178 | codec = NULL; | |
179 | } | |
180 | running = false; | |
181 | UNLOCK_D; | |
182 | #if LINUX || OSX || FREEBSD | |
183 | pthread_join(thread, NULL); | |
184 | #endif | |
185 | mutex_destroy(decode.mutex); | |
186 | } | |
187 | ||
188 | void decode_flush(void) { | |
189 | LOG_INFO("decode flush"); | |
190 | LOCK_D; | |
191 | decode.state = DECODE_STOPPED; | |
192 | IF_PROCESS( | |
193 | process_flush(); | |
194 | ); | |
195 | UNLOCK_D; | |
196 | } | |
197 | ||
198 | unsigned decode_newstream(unsigned sample_rate, unsigned supported_rates[]) { | |
199 | ||
200 | // called with O locked to get sample rate for potentially processed output stream | |
201 | // release O mutex during process_newstream as it can take some time | |
202 | ||
203 | MAY_PROCESS( | |
204 | if (decode.process) { | |
205 | UNLOCK_O; | |
206 | sample_rate = process_newstream(&decode.direct, sample_rate, supported_rates); | |
207 | LOCK_O; | |
208 | } | |
209 | ); | |
210 | ||
211 | return sample_rate; | |
212 | } | |
213 | ||
214 | void codec_open(u8_t format, u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness) { | |
215 | int i; | |
216 | ||
217 | LOG_INFO("codec open: '%c'", format); | |
218 | ||
219 | LOCK_D; | |
220 | ||
221 | decode.new_stream = true; | |
222 | decode.state = DECODE_STOPPED; | |
223 | ||
224 | MAY_PROCESS( | |
225 | decode.direct = true; // potentially changed within codec when processing enabled | |
226 | ); | |
227 | ||
228 | // find the required codec | |
229 | for (i = 0; i < MAX_CODECS; ++i) { | |
230 | ||
231 | if (codecs[i] && codecs[i]->id == format) { | |
232 | ||
233 | if (codec && codec != codecs[i]) { | |
234 | LOG_INFO("closing codec: '%c'", codec->id); | |
235 | codec->close(); | |
236 | } | |
237 | ||
238 | codec = codecs[i]; | |
239 | ||
240 | codec->open(sample_size, sample_rate, channels, endianness); | |
241 | ||
242 | decode.state = DECODE_READY; | |
243 | ||
244 | UNLOCK_D; | |
245 | return; | |
246 | } | |
247 | } | |
248 | ||
249 | UNLOCK_D; | |
250 | ||
251 | LOG_ERROR("codec not found"); | |
252 | } | |
253 |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012, 2013, triode1@btinternet.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 | // DSP over PCM (DOP) specific functions | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #if DSD | |
25 | ||
26 | extern struct buffer *outputbuf; | |
27 | extern struct outputstate output; | |
28 | ||
29 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
30 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
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) { | |
35 | int matched = 0; | |
36 | u32_t next = 0; | |
37 | ||
38 | while (frames--) { | |
39 | if (((*lptr & 0x00FF0000) == 0x00050000 && (*rptr & 0x00FF0000) == 0x00050000) || | |
40 | ((*lptr & 0x00FF0000) == 0x00FA0000 && (*rptr & 0x00FF0000) == 0x00FA0000)) { | |
41 | if (*lptr >> 24 == next) { | |
42 | matched++; | |
43 | next = ( 0x05 + 0xFA ) - next; | |
44 | } else { | |
45 | next = *lptr >> 24; | |
46 | matched = 1; | |
47 | } | |
48 | } else { | |
49 | return false; | |
50 | } | |
51 | if (matched == 32) { | |
52 | return true; | |
53 | } | |
54 | ||
55 | ++lptr; ++rptr; | |
56 | } | |
57 | return false; | |
58 | } | |
59 | ||
60 | // update the dop marker and potentially invert polarity for frames in the output buffer | |
61 | // performaned on all output including silence to maintain marker phase consitency | |
62 | void update_dop(u32_t *ptr, frames_t frames, bool invert) { | |
63 | static u32_t marker = 0x05; | |
64 | if (!invert) { | |
65 | while (frames--) { | |
66 | u32_t scaled_marker = marker << 24; | |
67 | *ptr = (*ptr & 0x00FFFFFF) | scaled_marker; | |
68 | ++ptr; | |
69 | *ptr = (*ptr & 0x00FFFFFF) | scaled_marker; | |
70 | ++ptr; | |
71 | marker = ( 0x05 + 0xFA ) - marker; | |
72 | } | |
73 | } else { | |
74 | while (frames--) { | |
75 | u32_t scaled_marker = marker << 24; | |
76 | *ptr = ((~(*ptr)) & 0x00FFFFFF) | scaled_marker; | |
77 | ++ptr; | |
78 | *ptr = ((~(*ptr)) & 0x00FFFFFF) | scaled_marker; | |
79 | ++ptr; | |
80 | marker = ( 0x05 + 0xFA ) - marker; | |
81 | } | |
82 | } | |
83 | } | |
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 | #endif // DSD |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // dsd support | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #if DSD | |
25 | ||
26 | // use dsd2pcm from Sebastian Gesemann for conversion to pcm: | |
27 | #include "./dsd2pcm/dsd2pcm.h" | |
28 | ||
29 | extern log_level loglevel; | |
30 | ||
31 | extern struct buffer *streambuf; | |
32 | extern struct buffer *outputbuf; | |
33 | extern struct streamstate stream; | |
34 | extern struct outputstate output; | |
35 | extern struct decodestate decode; | |
36 | extern struct processstate process; | |
37 | ||
38 | #define LOCK_S mutex_lock(streambuf->mutex) | |
39 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
40 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
41 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
42 | #if PROCESS | |
43 | #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex) | |
44 | #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex) | |
45 | #define LOCK_O_not_direct if (!decode.direct) mutex_lock(outputbuf->mutex) | |
46 | #define UNLOCK_O_not_direct if (!decode.direct) mutex_unlock(outputbuf->mutex) | |
47 | #define IF_DIRECT(x) if (decode.direct) { x } | |
48 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
49 | #else | |
50 | #define LOCK_O_direct mutex_lock(outputbuf->mutex) | |
51 | #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex) | |
52 | #define LOCK_O_not_direct | |
53 | #define UNLOCK_O_not_direct | |
54 | #define IF_DIRECT(x) { x } | |
55 | #define IF_PROCESS(x) | |
56 | #endif | |
57 | ||
58 | #define BLOCK 4096 // expected size of dsd block | |
59 | #define BLOCK_FRAMES BLOCK * BYTES_PER_FRAME | |
60 | #define WRAP_BUF_SIZE 16 | |
61 | ||
62 | typedef enum { UNKNOWN=0, DSF, DSDIFF } dsd_type; | |
63 | ||
64 | static bool dop = false; // local copy of output.has_dop to avoid holding output lock | |
65 | ||
66 | struct dsd { | |
67 | dsd_type type; | |
68 | u32_t consume; | |
69 | u32_t sample_rate; | |
70 | u32_t channels; | |
71 | u64_t sample_bytes; | |
72 | u32_t block_size; | |
73 | bool lsb_first; | |
74 | dsd2pcm_ctx *dsd2pcm_ctx[2]; | |
75 | float *transfer[2]; | |
76 | }; | |
77 | ||
78 | static struct dsd *d; | |
79 | ||
80 | static u64_t unpack64be(const u8_t *p) { | |
81 | return | |
82 | (u64_t)p[0] << 56 | (u64_t)p[1] << 48 | (u64_t)p[2] << 40 | (u64_t)p[3] << 32 | | |
83 | (u64_t)p[4] << 24 | (u64_t)p[5] << 16 | (u64_t)p[6] << 8 | (u64_t)p[7]; | |
84 | } | |
85 | ||
86 | static u64_t unpack64le(const u8_t *p) { | |
87 | return | |
88 | (u64_t)p[7] << 56 | (u64_t)p[6] << 48 | (u64_t)p[5] << 40 | (u64_t)p[4] << 32 | | |
89 | (u64_t)p[3] << 24 | (u64_t)p[2] << 16 | (u64_t)p[1] << 8 | (u64_t)p[0]; | |
90 | } | |
91 | ||
92 | static u32_t unpack32le(const u8_t *p) { | |
93 | return | |
94 | (u32_t)p[3] << 24 | (u32_t)p[2] << 16 | (u32_t)p[1] << 8 | (u32_t)p[0]; | |
95 | } | |
96 | ||
97 | static int _read_header(void) { | |
98 | unsigned bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
99 | s32_t consume; | |
100 | ||
101 | if (!d->type && bytes >= 4) { | |
102 | if (!memcmp(streambuf->readp, "FRM8", 4)) { | |
103 | d->type = DSDIFF; | |
104 | } else if (!memcmp(streambuf->readp, "DSD ", 4)) { | |
105 | d->type = DSF; | |
106 | } else { | |
107 | LOG_WARN("bad type"); | |
108 | return -1; | |
109 | } | |
110 | } | |
111 | ||
112 | while (bytes >= 16) { | |
113 | char id[5]; | |
114 | u64_t len = d->type == DSDIFF ? unpack64be(streambuf->readp + 4) : unpack64le(streambuf->readp + 4); | |
115 | memcpy(id, streambuf->readp, 4); | |
116 | id[4] = '\0'; | |
117 | consume = 0; | |
118 | ||
119 | if (d->type == DSDIFF) { | |
120 | if (!strcmp(id, "FRM8")) { | |
121 | if (!memcmp(streambuf->readp + 12, "DSD ", 4)) { | |
122 | consume = 16; // read into | |
123 | } else { | |
124 | LOG_WARN("bad dsdiff FRM8"); | |
125 | return -1; | |
126 | } | |
127 | } | |
128 | if (!strcmp(id, "PROP") && !memcmp(streambuf->readp + 12, "SND ", 4)) { | |
129 | consume = 16; // read into | |
130 | } | |
131 | if (!strcmp(id, "FVER")) { | |
132 | LOG_INFO("DSDIFF version: %u.%u.%u.%u", *(streambuf->readp + 12), *(streambuf->readp + 13), | |
133 | *(streambuf->readp + 14), *(streambuf->readp + 15)); | |
134 | } | |
135 | if (!strcmp(id, "FS ")) { | |
136 | d->sample_rate = unpackN((void *)(streambuf->readp + 12)); | |
137 | LOG_INFO("sample rate: %u", d->sample_rate); | |
138 | } | |
139 | if (!strcmp(id, "CHNL")) { | |
140 | d->channels = unpackn((void *)(streambuf->readp + 12)); | |
141 | LOG_INFO("channels: %u", d->channels); | |
142 | } | |
143 | if (!strcmp(id, "DSD ")) { | |
144 | LOG_INFO("found dsd len: " FMT_u64, len); | |
145 | d->sample_bytes = len; | |
146 | _buf_inc_readp(streambuf, 12); | |
147 | bytes -= 12; | |
148 | return 1; // got to the audio | |
149 | } | |
150 | } | |
151 | ||
152 | if (d->type == DSF) { | |
153 | if (!strcmp(id, "fmt ")) { | |
154 | if (bytes >= len && bytes >= 52) { | |
155 | u32_t version = unpack32le((void *)(streambuf->readp + 12)); | |
156 | u32_t format = unpack32le((void *)(streambuf->readp + 16)); | |
157 | LOG_INFO("DSF version: %u format: %u", version, format); | |
158 | if (format != 0) { | |
159 | LOG_WARN("only support DSD raw format"); | |
160 | return -1; | |
161 | } | |
162 | d->channels = unpack32le((void *)(streambuf->readp + 24)); | |
163 | d->sample_rate = unpack32le((void *)(streambuf->readp + 28)); | |
164 | d->lsb_first = (unpack32le((void *)(streambuf->readp + 32)) == 1); | |
165 | d->sample_bytes = unpack64le((void *)(streambuf->readp + 36)) / 8; | |
166 | d->block_size = unpack32le((void *)(streambuf->readp + 44)); | |
167 | LOG_INFO("channels: %u", d->channels); | |
168 | LOG_INFO("sample rate: %u", d->sample_rate); | |
169 | LOG_INFO("lsb first: %u", d->lsb_first); | |
170 | LOG_INFO("sample bytes: " FMT_u64, d->sample_bytes); | |
171 | LOG_INFO("block size: %u", d->block_size); | |
172 | } else { | |
173 | consume = -1; // come back later | |
174 | } | |
175 | } | |
176 | if (!strcmp(id, "data")) { | |
177 | LOG_INFO("found dsd len: " FMT_u64, len); | |
178 | _buf_inc_readp(streambuf, 12); | |
179 | bytes -= 12; | |
180 | return 1; // got to the audio | |
181 | } | |
182 | } | |
183 | ||
184 | // default to consuming whole chunk | |
185 | if (!consume) { | |
186 | consume = (s32_t)((d->type == DSDIFF) ? len + 12 : len); | |
187 | } | |
188 | ||
189 | if (bytes >= consume) { | |
190 | LOG_DEBUG("id: %s len: " FMT_u64 " consume: %d", id, len, consume); | |
191 | _buf_inc_readp(streambuf, consume); | |
192 | bytes -= consume; | |
193 | } else if (consume > 0) { | |
194 | LOG_DEBUG("id: %s len: " FMT_u64 " consume: %d - partial consume: %u", id, len, consume, bytes); | |
195 | _buf_inc_readp(streambuf, bytes); | |
196 | d->consume = consume - bytes; | |
197 | break; | |
198 | } else { | |
199 | break; | |
200 | } | |
201 | } | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
206 | static decode_state _decode_dsf(void) { | |
207 | ||
208 | // samples in streambuf are interleaved on block basis | |
209 | // we transfer whole blocks for all channels in one call and so itterate the while loop below to handle wraps | |
210 | ||
211 | unsigned bytes = _buf_used(streambuf); | |
212 | unsigned block_left = d->block_size; | |
213 | ||
214 | unsigned bytes_per_frame = dop ? 2 : 1; | |
215 | ||
216 | if (bytes < d->block_size * d->channels) { | |
217 | LOG_INFO("stream too short"); // this can occur when scanning the track | |
218 | return DECODE_COMPLETE; | |
219 | } | |
220 | ||
221 | IF_PROCESS( | |
222 | process.in_frames = 0; | |
223 | ); | |
224 | ||
225 | while (block_left) { | |
226 | ||
227 | frames_t frames, out, count; | |
228 | unsigned bytes_read; | |
229 | ||
230 | u8_t *iptrl = (u8_t *)streambuf->readp; | |
231 | u8_t *iptrr = (u8_t *)streambuf->readp + d->block_size; | |
232 | u32_t *optr; | |
233 | ||
234 | if (iptrr >= streambuf->wrap) { | |
235 | iptrr -= streambuf->size; | |
236 | } | |
237 | ||
238 | bytes = min(block_left, min(streambuf->wrap - iptrl, streambuf->wrap - iptrr)); | |
239 | ||
240 | IF_DIRECT( | |
241 | out = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; | |
242 | optr = (u32_t *)outputbuf->writep; | |
243 | ); | |
244 | IF_PROCESS( | |
245 | out = process.max_in_frames - process.in_frames; | |
246 | optr = (u32_t *)(process.inbuf + process.in_frames * BYTES_PER_FRAME); | |
247 | ); | |
248 | ||
249 | frames = min(bytes, d->sample_bytes) / bytes_per_frame; | |
250 | 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; | |
254 | frames = 1; | |
255 | } else { | |
256 | // should not get here due to wrapping m/2 for dop should never result in 0 as header len is always even | |
257 | LOG_INFO("frames got to zero"); | |
258 | return DECODE_COMPLETE; | |
259 | } | |
260 | } | |
261 | ||
262 | frames = min(frames, out); | |
263 | frames = min(frames, BLOCK); | |
264 | bytes_read = frames * bytes_per_frame; | |
265 | ||
266 | count = frames; | |
267 | ||
268 | if (dop) { | |
269 | ||
270 | if (d->channels == 1) { | |
271 | if (d->lsb_first) { | |
272 | while (count--) { | |
273 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 16 | dsd2pcm_bitreverse[*(iptrl+1)] << 8; | |
274 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 16 | dsd2pcm_bitreverse[*(iptrl+1)] << 8; | |
275 | iptrl += 2; | |
276 | } | |
277 | } else { | |
278 | while (count--) { | |
279 | *(optr++) = *(iptrl) << 16 | *(iptrl+1) << 8; | |
280 | *(optr++) = *(iptrl) << 16 | *(iptrl+1) << 8; | |
281 | iptrl += 2; | |
282 | } | |
283 | } | |
284 | } else { | |
285 | if (d->lsb_first) { | |
286 | while (count--) { | |
287 | *(optr++) = dsd2pcm_bitreverse[*(iptrl)] << 16 | dsd2pcm_bitreverse[*(iptrl+1)] << 8; | |
288 | *(optr++) = dsd2pcm_bitreverse[*(iptrr)] << 16 | dsd2pcm_bitreverse[*(iptrr+1)] << 8; | |
289 | iptrl += 2; | |
290 | iptrr += 2; | |
291 | } | |
292 | } else { | |
293 | while (count--) { | |
294 | *(optr++) = *(iptrl) << 16 | *(iptrl+1) << 8; | |
295 | *(optr++) = *(iptrr) << 16 | *(iptrr+1) << 8; | |
296 | iptrl += 2; | |
297 | iptrr += 2; | |
298 | } | |
299 | } | |
300 | } | |
301 | ||
302 | } else { | |
303 | ||
304 | if (d->channels == 1) { | |
305 | float *iptrf = d->transfer[0]; | |
306 | dsd2pcm_translate(d->dsd2pcm_ctx[0], frames, iptrl, 1, d->lsb_first, iptrf, 1); | |
307 | while (count--) { | |
308 | double scaled = *iptrf++ * 0x7fffffff; | |
309 | if (scaled > 2147483647.0) scaled = 2147483647.0; | |
310 | if (scaled < -2147483648.0) scaled = -2147483648.0; | |
311 | *optr++ = (s32_t)scaled; | |
312 | *optr++ = (s32_t)scaled; | |
313 | } | |
314 | } else { | |
315 | float *iptrfl = d->transfer[0]; | |
316 | float *iptrfr = d->transfer[1]; | |
317 | dsd2pcm_translate(d->dsd2pcm_ctx[0], frames, iptrl, 1, d->lsb_first, iptrfl, 1); | |
318 | dsd2pcm_translate(d->dsd2pcm_ctx[1], frames, iptrr, 1, d->lsb_first, iptrfr, 1); | |
319 | while (count--) { | |
320 | double scaledl = *iptrfl++ * 0x7fffffff; | |
321 | double scaledr = *iptrfr++ * 0x7fffffff; | |
322 | if (scaledl > 2147483647.0) scaledl = 2147483647.0; | |
323 | if (scaledl < -2147483648.0) scaledl = -2147483648.0; | |
324 | if (scaledr > 2147483647.0) scaledr = 2147483647.0; | |
325 | if (scaledr < -2147483648.0) scaledr = -2147483648.0; | |
326 | *optr++ = (s32_t)scaledl; | |
327 | *optr++ = (s32_t)scaledr; | |
328 | } | |
329 | } | |
330 | ||
331 | } | |
332 | ||
333 | _buf_inc_readp(streambuf, bytes_read); | |
334 | ||
335 | block_left -= bytes_read; | |
336 | ||
337 | if (d->sample_bytes > bytes_read) { | |
338 | d->sample_bytes -= bytes_read; | |
339 | } else { | |
340 | LOG_INFO("end of track samples"); | |
341 | block_left = 0; | |
342 | d->sample_bytes = 0; | |
343 | } | |
344 | ||
345 | IF_DIRECT( | |
346 | _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); | |
347 | ); | |
348 | IF_PROCESS( | |
349 | process.in_frames += frames; | |
350 | ); | |
351 | ||
352 | LOG_SDEBUG("write %u frames", frames); | |
353 | } | |
354 | ||
355 | // skip the other channel blocks | |
356 | // the right channel has already been read and is guarenteed to be in streambuf so can be skipped immediately | |
357 | if (d->channels > 1) { | |
358 | _buf_inc_readp(streambuf, d->block_size); | |
359 | } | |
360 | if (d->channels > 2) { | |
361 | d->consume = d->block_size * (d->channels - 2); | |
362 | } | |
363 | ||
364 | return DECODE_RUNNING; | |
365 | } | |
366 | ||
367 | static decode_state _decode_dsdiff(void) { | |
368 | ||
369 | // samples in streambuf are interleaved on byte per channel | |
370 | // we process as little as necessary per call and only need to handle frames wrapping round streambuf | |
371 | ||
372 | unsigned bytes_per_frame, bytes_read; | |
373 | frames_t out, frames, count; | |
374 | u8_t *iptr; | |
375 | u32_t *optr; | |
376 | u8_t tmp[WRAP_BUF_SIZE]; | |
377 | ||
378 | unsigned bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
379 | ||
380 | IF_DIRECT( | |
381 | out = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; | |
382 | ); | |
383 | IF_PROCESS( | |
384 | out = process.max_in_frames; | |
385 | ); | |
386 | ||
387 | if (dop) { | |
388 | bytes_per_frame = d->channels * 2; | |
389 | } else { | |
390 | bytes_per_frame = d->channels; | |
391 | out = min(out, BLOCK); | |
392 | } | |
393 | ||
394 | frames = min(min(bytes, d->sample_bytes) / bytes_per_frame, out); | |
395 | bytes_read = frames * bytes_per_frame; | |
396 | ||
397 | iptr = (u8_t *)streambuf->readp; | |
398 | ||
399 | IF_DIRECT( | |
400 | optr = (u32_t *)outputbuf->writep; | |
401 | ); | |
402 | IF_PROCESS( | |
403 | optr = (u32_t *)process.inbuf; | |
404 | ); | |
405 | ||
406 | // handle wrap around end of streambuf and partial dop frame at end of stream | |
407 | if (!frames && bytes < bytes_per_frame) { | |
408 | memset(tmp, 0x69, WRAP_BUF_SIZE); // 0x69 = dsd silence | |
409 | memcpy(tmp, streambuf->readp, bytes); | |
410 | if (_buf_used(streambuf) > bytes_per_frame) { | |
411 | memcpy(tmp + bytes, streambuf->buf, bytes_per_frame - bytes); | |
412 | bytes_read = bytes_per_frame; | |
413 | } else { | |
414 | bytes_read = bytes; | |
415 | } | |
416 | iptr = tmp; | |
417 | frames = 1; | |
418 | } | |
419 | ||
420 | count = frames; | |
421 | ||
422 | if (dop) { | |
423 | ||
424 | if (d->channels == 1) { | |
425 | while (count--) { | |
426 | *(optr++) = *(iptr) << 16 | *(iptr+1) << 8; | |
427 | *(optr++) = *(iptr) << 16 | *(iptr+1) << 8; | |
428 | iptr += bytes_per_frame; | |
429 | } | |
430 | } else { | |
431 | while (count--) { | |
432 | *(optr++) = *(iptr ) << 16 | *(iptr + d->channels) << 8; | |
433 | *(optr++) = *(iptr+1) << 16 | *(iptr + d->channels + 1) << 8; | |
434 | iptr += bytes_per_frame; | |
435 | } | |
436 | } | |
437 | ||
438 | } else { | |
439 | ||
440 | if (d->channels == 1) { | |
441 | float *iptrf = d->transfer[0]; | |
442 | dsd2pcm_translate(d->dsd2pcm_ctx[0], frames, iptr, 1, 0, iptrf, 1); | |
443 | while (count--) { | |
444 | double scaled = *iptrf++ * 0x7fffffff; | |
445 | if (scaled > 2147483647.0) scaled = 2147483647.0; | |
446 | if (scaled < -2147483648.0) scaled = -2147483648.0; | |
447 | *optr++ = (s32_t)scaled; | |
448 | *optr++ = (s32_t)scaled; | |
449 | } | |
450 | } else { | |
451 | float *iptrfl = d->transfer[0]; | |
452 | float *iptrfr = d->transfer[1]; | |
453 | dsd2pcm_translate(d->dsd2pcm_ctx[0], frames, iptr, d->channels, 0, iptrfl, 1); | |
454 | dsd2pcm_translate(d->dsd2pcm_ctx[1], frames, iptr + 1, d->channels, 0, iptrfr, 1); | |
455 | while (count--) { | |
456 | double scaledl = *iptrfl++ * 0x7fffffff; | |
457 | double scaledr = *iptrfr++ * 0x7fffffff; | |
458 | if (scaledl > 2147483647.0) scaledl = 2147483647.0; | |
459 | if (scaledl < -2147483648.0) scaledl = -2147483648.0; | |
460 | if (scaledr > 2147483647.0) scaledr = 2147483647.0; | |
461 | if (scaledr < -2147483648.0) scaledr = -2147483648.0; | |
462 | *optr++ = (s32_t)scaledl; | |
463 | *optr++ = (s32_t)scaledr; | |
464 | } | |
465 | } | |
466 | ||
467 | } | |
468 | ||
469 | _buf_inc_readp(streambuf, bytes_read); | |
470 | ||
471 | if (d->sample_bytes > bytes_read) { | |
472 | d->sample_bytes -= bytes_read; | |
473 | } else { | |
474 | LOG_INFO("end of track samples"); | |
475 | d->sample_bytes = 0; | |
476 | } | |
477 | ||
478 | IF_DIRECT( | |
479 | _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); | |
480 | ); | |
481 | IF_PROCESS( | |
482 | process.in_frames = frames; | |
483 | ); | |
484 | ||
485 | LOG_SDEBUG("write %u frames", frames); | |
486 | ||
487 | return DECODE_RUNNING; | |
488 | } | |
489 | ||
490 | ||
491 | static decode_state dsd_decode(void) { | |
492 | decode_state ret; | |
493 | ||
494 | LOCK_S; | |
495 | ||
496 | if ((stream.state <= DISCONNECT && !_buf_used(streambuf)) || (!decode.new_stream && d->sample_bytes == 0)) { | |
497 | UNLOCK_S; | |
498 | return DECODE_COMPLETE; | |
499 | } | |
500 | ||
501 | if (d->consume) { | |
502 | unsigned consume = min(d->consume, min(_buf_used(streambuf), _buf_cont_read(streambuf))); | |
503 | LOG_DEBUG("consume: %u of %u", consume, d->consume); | |
504 | _buf_inc_readp(streambuf, consume); | |
505 | d->consume -= consume; | |
506 | if (d->consume) { | |
507 | UNLOCK_S; | |
508 | return DECODE_RUNNING; | |
509 | } | |
510 | } | |
511 | ||
512 | if (decode.new_stream) { | |
513 | int r = _read_header(); | |
514 | if (r < 1) { | |
515 | UNLOCK_S; | |
516 | return DECODE_ERROR; | |
517 | } | |
518 | if (r == 0) { | |
519 | UNLOCK_S; | |
520 | return DECODE_RUNNING; | |
521 | } | |
522 | // otherwise got to start of audio | |
523 | ||
524 | LOCK_O; | |
525 | ||
526 | LOG_INFO("setting track_start"); | |
527 | output.track_start = outputbuf->writep; | |
528 | ||
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; | |
539 | output.next_sample_rate = d->sample_rate / 16; | |
540 | output.fade = FADE_INACTIVE; | |
541 | } else { | |
542 | LOG_INFO("DSD to PCM output"); | |
543 | output.next_dop = false; | |
544 | output.next_sample_rate = decode_newstream(d->sample_rate / 8, output.supported_rates); | |
545 | if (output.fade_mode) _checkfade(true); | |
546 | } | |
547 | ||
548 | decode.new_stream = false; | |
549 | ||
550 | UNLOCK_O; | |
551 | } | |
552 | ||
553 | LOCK_O_direct; | |
554 | ||
555 | switch (d->type) { | |
556 | case DSF: | |
557 | ret = _decode_dsf(); | |
558 | break; | |
559 | case DSDIFF: | |
560 | ret = _decode_dsdiff(); | |
561 | break; | |
562 | default: | |
563 | ret = DECODE_ERROR; | |
564 | } | |
565 | ||
566 | UNLOCK_O_direct; | |
567 | UNLOCK_S; | |
568 | ||
569 | return ret; | |
570 | } | |
571 | ||
572 | static void dsd_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
573 | d->type = UNKNOWN; | |
574 | ||
575 | if (!d->dsd2pcm_ctx[0]) { | |
576 | d->dsd2pcm_ctx[0] = dsd2pcm_init(); | |
577 | d->dsd2pcm_ctx[1] = dsd2pcm_init(); | |
578 | } else { | |
579 | dsd2pcm_reset(d->dsd2pcm_ctx[0]); | |
580 | dsd2pcm_reset(d->dsd2pcm_ctx[1]); | |
581 | } | |
582 | if (!d->transfer[1]) { | |
583 | d->transfer[0] = malloc(sizeof(float) * BLOCK); | |
584 | d->transfer[1] = malloc(sizeof(float) * BLOCK); | |
585 | } | |
586 | } | |
587 | ||
588 | static void dsd_close(void) { | |
589 | if (d->dsd2pcm_ctx[0]) { | |
590 | dsd2pcm_destroy(d->dsd2pcm_ctx[0]); | |
591 | dsd2pcm_destroy(d->dsd2pcm_ctx[1]); | |
592 | d->dsd2pcm_ctx[0] = NULL; | |
593 | d->dsd2pcm_ctx[1] = NULL; | |
594 | } | |
595 | if (d->transfer[0]) { | |
596 | free(d->transfer[0]); | |
597 | free(d->transfer[1]); | |
598 | d->transfer[0] = NULL; | |
599 | d->transfer[1] = NULL; | |
600 | } | |
601 | } | |
602 | ||
603 | struct codec *register_dsd(void) { | |
604 | static struct codec ret = { | |
605 | 'd', // id | |
606 | "dsf,dff", // types | |
607 | BLOCK * 2, // min read | |
608 | BLOCK_FRAMES,// min space | |
609 | dsd_open, // open | |
610 | dsd_close, // close | |
611 | dsd_decode, // decode | |
612 | }; | |
613 | ||
614 | d = malloc(sizeof(struct dsd)); | |
615 | if (!d) { | |
616 | return NULL; | |
617 | } | |
618 | ||
619 | memset(d, 0, sizeof(struct dsd)); | |
620 | ||
621 | dsd2pcm_precalc(); | |
622 | ||
623 | LOG_INFO("using dsd to decode dsf,dff"); | |
624 | return &ret; | |
625 | } | |
626 | ||
627 | #endif // DSD |
0 | Copyright 2009, 2011 Sebastian Gesemann. All rights reserved. | |
1 | ||
2 | Redistribution and use in source and binary forms, with or without modification, are | |
3 | permitted provided that the following conditions are met: | |
4 | ||
5 | 1. Redistributions of source code must retain the above copyright notice, this list of | |
6 | conditions and the following disclaimer. | |
7 | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list | |
9 | of conditions and the following disclaimer in the documentation and/or other materials | |
10 | provided with the distribution. | |
11 | ||
12 | THIS SOFTWARE IS PROVIDED BY SEBASTIAN GESEMANN ''AS IS'' AND ANY EXPRESS OR IMPLIED | |
13 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
14 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN GESEMANN OR | |
15 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
16 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
17 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
18 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
19 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
20 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
21 | ||
22 | The views and conclusions contained in the software and documentation are those of the | |
23 | authors and should not be interpreted as representing official policies, either expressed | |
24 | or implied, of Sebastian Gesemann. |
0 | /* | |
1 | ||
2 | Copyright 2009, 2011 Sebastian Gesemann. All rights reserved. | |
3 | ||
4 | Redistribution and use in source and binary forms, with or without modification, are | |
5 | permitted provided that the following conditions are met: | |
6 | ||
7 | 1. Redistributions of source code must retain the above copyright notice, this list of | |
8 | conditions and the following disclaimer. | |
9 | ||
10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list | |
11 | of conditions and the following disclaimer in the documentation and/or other materials | |
12 | provided with the distribution. | |
13 | ||
14 | THIS SOFTWARE IS PROVIDED BY SEBASTIAN GESEMANN ''AS IS'' AND ANY EXPRESS OR IMPLIED | |
15 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
16 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN GESEMANN OR | |
17 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
18 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
20 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
21 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
22 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
23 | ||
24 | The views and conclusions contained in the software and documentation are those of the | |
25 | authors and should not be interpreted as representing official policies, either expressed | |
26 | or implied, of Sebastian Gesemann. | |
27 | ||
28 | ---- | |
29 | ||
30 | Additions (c) Adrian Smith, 2013 under same licence terms: | |
31 | - expose bitreverse array as dsd2pcm_bitreverse | |
32 | - expose precalc function as dsd2pcm_precalc to allow it to be initalised | |
33 | ||
34 | */ | |
35 | ||
36 | #include <stdlib.h> | |
37 | #include <string.h> | |
38 | ||
39 | #include "dsd2pcm.h" | |
40 | ||
41 | #define HTAPS 48 /* number of FIR constants */ | |
42 | #define FIFOSIZE 16 /* must be a power of two */ | |
43 | #define FIFOMASK (FIFOSIZE-1) /* bit mask for FIFO offsets */ | |
44 | #define CTABLES ((HTAPS+7)/8) /* number of "8 MACs" lookup tables */ | |
45 | ||
46 | #if FIFOSIZE*8 < HTAPS*2 | |
47 | #error "FIFOSIZE too small" | |
48 | #endif | |
49 | ||
50 | /* | |
51 | * Properties of this 96-tap lowpass filter when applied on a signal | |
52 | * with sampling rate of 44100*64 Hz: | |
53 | * | |
54 | * () has a delay of 17 microseconds. | |
55 | * | |
56 | * () flat response up to 48 kHz | |
57 | * | |
58 | * () if you downsample afterwards by a factor of 8, the | |
59 | * spectrum below 70 kHz is practically alias-free. | |
60 | * | |
61 | * () stopband rejection is about 160 dB | |
62 | * | |
63 | * The coefficient tables ("ctables") take only 6 Kibi Bytes and | |
64 | * should fit into a modern processor's fast cache. | |
65 | */ | |
66 | ||
67 | /* | |
68 | * The 2nd half (48 coeffs) of a 96-tap symmetric lowpass filter | |
69 | */ | |
70 | static const double htaps[HTAPS] = { | |
71 | 0.09950731974056658, | |
72 | 0.09562845727714668, | |
73 | 0.08819647126516944, | |
74 | 0.07782552527068175, | |
75 | 0.06534876523171299, | |
76 | 0.05172629311427257, | |
77 | 0.0379429484910187, | |
78 | 0.02490921351762261, | |
79 | 0.0133774746265897, | |
80 | 0.003883043418804416, | |
81 | -0.003284703416210726, | |
82 | -0.008080250212687497, | |
83 | -0.01067241812471033, | |
84 | -0.01139427235000863, | |
85 | -0.0106813877974587, | |
86 | -0.009007905078766049, | |
87 | -0.006828859761015335, | |
88 | -0.004535184322001496, | |
89 | -0.002425035959059578, | |
90 | -0.0006922187080790708, | |
91 | 0.0005700762133516592, | |
92 | 0.001353838005269448, | |
93 | 0.001713709169690937, | |
94 | 0.001742046839472948, | |
95 | 0.001545601648013235, | |
96 | 0.001226696225277855, | |
97 | 0.0008704322683580222, | |
98 | 0.0005381636200535649, | |
99 | 0.000266446345425276, | |
100 | 7.002968738383528e-05, | |
101 | -5.279407053811266e-05, | |
102 | -0.0001140625650874684, | |
103 | -0.0001304796361231895, | |
104 | -0.0001189970287491285, | |
105 | -9.396247155265073e-05, | |
106 | -6.577634378272832e-05, | |
107 | -4.07492895872535e-05, | |
108 | -2.17407957554587e-05, | |
109 | -9.163058931391722e-06, | |
110 | -2.017460145032201e-06, | |
111 | 1.249721855219005e-06, | |
112 | 2.166655190537392e-06, | |
113 | 1.930520892991082e-06, | |
114 | 1.319400334374195e-06, | |
115 | 7.410039764949091e-07, | |
116 | 3.423230509967409e-07, | |
117 | 1.244182214744588e-07, | |
118 | 3.130441005359396e-08 | |
119 | }; | |
120 | ||
121 | static float ctables[CTABLES][256]; | |
122 | unsigned char dsd2pcm_bitreverse[256]; | |
123 | static int precalculated = 0; | |
124 | ||
125 | void dsd2pcm_precalc(void) | |
126 | { | |
127 | int t, e, m, k; | |
128 | double acc; | |
129 | if (precalculated) return; | |
130 | for (t=0, e=0; t<256; ++t) { | |
131 | dsd2pcm_bitreverse[t] = e; | |
132 | for (m=128; m && !((e^=m)&m); m>>=1) | |
133 | ; | |
134 | } | |
135 | for (t=0; t<CTABLES; ++t) { | |
136 | k = HTAPS - t*8; | |
137 | if (k>8) k=8; | |
138 | for (e=0; e<256; ++e) { | |
139 | acc = 0.0; | |
140 | for (m=0; m<k; ++m) { | |
141 | acc += (((e >> (7-m)) & 1)*2-1) * htaps[t*8+m]; | |
142 | } | |
143 | ctables[CTABLES-1-t][e] = (float)acc; | |
144 | } | |
145 | } | |
146 | precalculated = 1; | |
147 | } | |
148 | ||
149 | struct dsd2pcm_ctx_s | |
150 | { | |
151 | unsigned char fifo[FIFOSIZE]; | |
152 | unsigned fifopos; | |
153 | }; | |
154 | ||
155 | extern dsd2pcm_ctx* dsd2pcm_init() | |
156 | { | |
157 | dsd2pcm_ctx* ptr; | |
158 | if (!precalculated) dsd2pcm_precalc(); | |
159 | ptr = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx)); | |
160 | if (ptr) dsd2pcm_reset(ptr); | |
161 | return ptr; | |
162 | } | |
163 | ||
164 | extern void dsd2pcm_destroy(dsd2pcm_ctx* ptr) | |
165 | { | |
166 | free(ptr); | |
167 | } | |
168 | ||
169 | extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx* ptr) | |
170 | { | |
171 | dsd2pcm_ctx* p2; | |
172 | p2 = (dsd2pcm_ctx*) malloc(sizeof(dsd2pcm_ctx)); | |
173 | if (p2) { | |
174 | memcpy(p2,ptr,sizeof(dsd2pcm_ctx)); | |
175 | } | |
176 | return p2; | |
177 | } | |
178 | ||
179 | extern void dsd2pcm_reset(dsd2pcm_ctx* ptr) | |
180 | { | |
181 | int i; | |
182 | for (i=0; i<FIFOSIZE; ++i) | |
183 | ptr->fifo[i] = 0x69; /* my favorite silence pattern */ | |
184 | ptr->fifopos = 0; | |
185 | /* 0x69 = 01101001 | |
186 | * This pattern "on repeat" makes a low energy 352.8 kHz tone | |
187 | * and a high energy 1.0584 MHz tone which should be filtered | |
188 | * out completely by any playback system --> silence | |
189 | */ | |
190 | } | |
191 | ||
192 | extern void dsd2pcm_translate( | |
193 | dsd2pcm_ctx* ptr, | |
194 | size_t samples, | |
195 | const unsigned char *src, ptrdiff_t src_stride, | |
196 | int lsbf, | |
197 | float *dst, ptrdiff_t dst_stride) | |
198 | { | |
199 | unsigned ffp; | |
200 | unsigned i; | |
201 | unsigned bite1, bite2; | |
202 | unsigned char* p; | |
203 | double acc; | |
204 | ffp = ptr->fifopos; | |
205 | lsbf = lsbf ? 1 : 0; | |
206 | while (samples-- > 0) { | |
207 | bite1 = *src & 0xFFu; | |
208 | if (lsbf) bite1 = dsd2pcm_bitreverse[bite1]; | |
209 | ptr->fifo[ffp] = bite1; src += src_stride; | |
210 | p = ptr->fifo + ((ffp-CTABLES) & FIFOMASK); | |
211 | *p = dsd2pcm_bitreverse[*p & 0xFF]; | |
212 | acc = 0; | |
213 | for (i=0; i<CTABLES; ++i) { | |
214 | bite1 = ptr->fifo[(ffp -i) & FIFOMASK] & 0xFF; | |
215 | bite2 = ptr->fifo[(ffp-(CTABLES*2-1)+i) & FIFOMASK] & 0xFF; | |
216 | acc += ctables[i][bite1] + ctables[i][bite2]; | |
217 | } | |
218 | *dst = (float)acc; dst += dst_stride; | |
219 | ffp = (ffp + 1) & FIFOMASK; | |
220 | } | |
221 | ptr->fifopos = ffp; | |
222 | } | |
223 |
0 | /* | |
1 | ||
2 | Copyright 2009, 2011 Sebastian Gesemann. All rights reserved. | |
3 | ||
4 | Redistribution and use in source and binary forms, with or without modification, are | |
5 | permitted provided that the following conditions are met: | |
6 | ||
7 | 1. Redistributions of source code must retain the above copyright notice, this list of | |
8 | conditions and the following disclaimer. | |
9 | ||
10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list | |
11 | of conditions and the following disclaimer in the documentation and/or other materials | |
12 | provided with the distribution. | |
13 | ||
14 | THIS SOFTWARE IS PROVIDED BY SEBASTIAN GESEMANN ''AS IS'' AND ANY EXPRESS OR IMPLIED | |
15 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
16 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN GESEMANN OR | |
17 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
18 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
19 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | |
20 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
21 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
22 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
23 | ||
24 | The views and conclusions contained in the software and documentation are those of the | |
25 | authors and should not be interpreted as representing official policies, either expressed | |
26 | or implied, of Sebastian Gesemann. | |
27 | ||
28 | ---- | |
29 | ||
30 | Marked additions (c) Adrian Smith, 2013 under same licence terms | |
31 | ||
32 | */ | |
33 | ||
34 | #ifndef DSD2PCM_H_INCLUDED | |
35 | #define DSD2PCM_H_INCLUDED | |
36 | ||
37 | #include <stddef.h> | |
38 | #include <string.h> | |
39 | ||
40 | #ifdef __cplusplus | |
41 | extern "C" { | |
42 | #endif | |
43 | ||
44 | struct dsd2pcm_ctx_s; | |
45 | ||
46 | typedef struct dsd2pcm_ctx_s dsd2pcm_ctx; | |
47 | ||
48 | /** | |
49 | * initializes a "dsd2pcm engine" for one channel | |
50 | * (precomputes tables and allocates memory) | |
51 | * | |
52 | * This is the only function that is not thread-safe in terms of the | |
53 | * POSIX thread-safety definition because it modifies global state | |
54 | * (lookup tables are computed during the first call) | |
55 | */ | |
56 | extern dsd2pcm_ctx* dsd2pcm_init(void); | |
57 | ||
58 | /** | |
59 | * deinitializes a "dsd2pcm engine" | |
60 | * (releases memory, don't forget!) | |
61 | */ | |
62 | extern void dsd2pcm_destroy(dsd2pcm_ctx *ctx); | |
63 | ||
64 | /** | |
65 | * clones the context and returns a pointer to the | |
66 | * newly allocated copy | |
67 | */ | |
68 | extern dsd2pcm_ctx* dsd2pcm_clone(dsd2pcm_ctx *ctx); | |
69 | ||
70 | /** | |
71 | * resets the internal state for a fresh new stream | |
72 | */ | |
73 | extern void dsd2pcm_reset(dsd2pcm_ctx *ctx); | |
74 | ||
75 | /** | |
76 | * "translates" a stream of octets to a stream of floats | |
77 | * (8:1 decimation) | |
78 | * @param ctx -- pointer to abstract context (buffers) | |
79 | * @param samples -- number of octets/samples to "translate" | |
80 | * @param src -- pointer to first octet (input) | |
81 | * @param src_stride -- src pointer increment | |
82 | * @param lsbitfirst -- bitorder, 0=msb first, 1=lsbfirst | |
83 | * @param dst -- pointer to first float (output) | |
84 | * @param dst_stride -- dst pointer increment | |
85 | */ | |
86 | extern void dsd2pcm_translate(dsd2pcm_ctx *ctx, | |
87 | size_t samples, | |
88 | const unsigned char *src, ptrdiff_t src_stride, | |
89 | int lsbitfirst, | |
90 | float *dst, ptrdiff_t dst_stride); | |
91 | ||
92 | /** | |
93 | * Additions by Adrian Smith (c) 2013 for Squeezelite | |
94 | */ | |
95 | extern unsigned char dsd2pcm_bitreverse[]; | |
96 | ||
97 | extern void dsd2pcm_precalc(void); | |
98 | /** | |
99 | * End of addition | |
100 | */ | |
101 | ||
102 | #ifdef __cplusplus | |
103 | } /* extern "C" */ | |
104 | #endif | |
105 | ||
106 | #endif /* include guard DSD2PCM_H_INCLUDED */ | |
107 |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 "squeezelite.h" | |
21 | ||
22 | #include <neaacdec.h> | |
23 | ||
24 | #define WRAPBUF_LEN 2048 | |
25 | ||
26 | struct chunk_table { | |
27 | u32_t sample, offset; | |
28 | }; | |
29 | ||
30 | struct faad { | |
31 | NeAACDecHandle hAac; | |
32 | u8_t type; | |
33 | // following used for mp4 only | |
34 | u32_t consume; | |
35 | u32_t pos; | |
36 | u32_t sample; | |
37 | u32_t nextchunk; | |
38 | void *stsc; | |
39 | u32_t skip; | |
40 | u64_t samples; | |
41 | u64_t sttssamples; | |
42 | bool empty; | |
43 | struct chunk_table *chunkinfo; | |
44 | // faad symbols to be dynamically loaded | |
45 | #if !LINKALL | |
46 | NeAACDecConfigurationPtr (* NeAACDecGetCurrentConfiguration)(NeAACDecHandle); | |
47 | unsigned char (* NeAACDecSetConfiguration)(NeAACDecHandle, NeAACDecConfigurationPtr); | |
48 | NeAACDecHandle (* NeAACDecOpen)(void); | |
49 | void (* NeAACDecClose)(NeAACDecHandle); | |
50 | long (* NeAACDecInit)(NeAACDecHandle, unsigned char *, unsigned long, unsigned long *, unsigned char *); | |
51 | char (* NeAACDecInit2)(NeAACDecHandle, unsigned char *pBuffer, unsigned long, unsigned long *, unsigned char *); | |
52 | void *(* NeAACDecDecode)(NeAACDecHandle, NeAACDecFrameInfo *, unsigned char *, unsigned long); | |
53 | char *(* NeAACDecGetErrorMessage)(unsigned char); | |
54 | #endif | |
55 | }; | |
56 | ||
57 | static struct faad *a; | |
58 | ||
59 | extern log_level loglevel; | |
60 | ||
61 | extern struct buffer *streambuf; | |
62 | extern struct buffer *outputbuf; | |
63 | extern struct streamstate stream; | |
64 | extern struct outputstate output; | |
65 | extern struct decodestate decode; | |
66 | extern struct processstate process; | |
67 | ||
68 | #define LOCK_S mutex_lock(streambuf->mutex) | |
69 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
70 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
71 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
72 | #if PROCESS | |
73 | #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex) | |
74 | #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex) | |
75 | #define IF_DIRECT(x) if (decode.direct) { x } | |
76 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
77 | #else | |
78 | #define LOCK_O_direct mutex_lock(outputbuf->mutex) | |
79 | #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex) | |
80 | #define IF_DIRECT(x) { x } | |
81 | #define IF_PROCESS(x) | |
82 | #endif | |
83 | ||
84 | #if LINKALL | |
85 | #define NEAAC(h, fn, ...) (NeAACDec ## fn)(__VA_ARGS__) | |
86 | #else | |
87 | #define NEAAC(h, fn, ...) (h)->NeAACDec##fn(__VA_ARGS__) | |
88 | #endif | |
89 | ||
90 | // minimal code for mp4 file parsing to extract audio config and find media data | |
91 | ||
92 | // adapted from faad2/common/mp4ff | |
93 | u32_t mp4_desc_length(u8_t **buf) { | |
94 | u8_t b; | |
95 | u8_t num_bytes = 0; | |
96 | u32_t length = 0; | |
97 | ||
98 | do { | |
99 | b = **buf; | |
100 | *buf += 1; | |
101 | num_bytes++; | |
102 | length = (length << 7) | (b & 0x7f); | |
103 | } while ((b & 0x80) && num_bytes < 4); | |
104 | ||
105 | return length; | |
106 | } | |
107 | ||
108 | // read mp4 header to extract config data | |
109 | static int read_mp4_header(unsigned long *samplerate_p, unsigned char *channels_p) { | |
110 | size_t bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
111 | char type[5]; | |
112 | u32_t len; | |
113 | ||
114 | while (bytes >= 8) { | |
115 | // count trak to find the first playable one | |
116 | static unsigned trak, play; | |
117 | u32_t consume; | |
118 | ||
119 | len = unpackN((u32_t *)streambuf->readp); | |
120 | memcpy(type, streambuf->readp + 4, 4); | |
121 | type[4] = '\0'; | |
122 | ||
123 | if (!strcmp(type, "moov")) { | |
124 | trak = 0; | |
125 | play = 0; | |
126 | } | |
127 | if (!strcmp(type, "trak")) { | |
128 | trak++; | |
129 | } | |
130 | ||
131 | // extract audio config from within esds and pass to DecInit2 | |
132 | if (!strcmp(type, "esds") && bytes > len) { | |
133 | unsigned config_len; | |
134 | u8_t *ptr = streambuf->readp + 12; | |
135 | if (*ptr++ == 0x03) { | |
136 | mp4_desc_length(&ptr); | |
137 | ptr += 4; | |
138 | } else { | |
139 | ptr += 3; | |
140 | } | |
141 | mp4_desc_length(&ptr); | |
142 | ptr += 13; | |
143 | if (*ptr++ != 0x05) { | |
144 | LOG_WARN("error parsing esds"); | |
145 | return -1; | |
146 | } | |
147 | config_len = mp4_desc_length(&ptr); | |
148 | if (NEAAC(a, Init2, a->hAac, ptr, config_len, samplerate_p, channels_p) == 0) { | |
149 | LOG_DEBUG("playable aac track: %u", trak); | |
150 | play = trak; | |
151 | } | |
152 | } | |
153 | ||
154 | // extract the total number of samples from stts | |
155 | if (!strcmp(type, "stts") && bytes > len) { | |
156 | u32_t i; | |
157 | u8_t *ptr = streambuf->readp + 12; | |
158 | u32_t entries = unpackN((u32_t *)ptr); | |
159 | ptr += 4; | |
160 | for (i = 0; i < entries; ++i) { | |
161 | u32_t count = unpackN((u32_t *)ptr); | |
162 | u32_t size = unpackN((u32_t *)(ptr + 4)); | |
163 | a->sttssamples += count * size; | |
164 | ptr += 8; | |
165 | } | |
166 | LOG_DEBUG("total number of samples contained in stts: " FMT_u64, a->sttssamples); | |
167 | } | |
168 | ||
169 | // stash sample to chunk info, assume it comes before stco | |
170 | if (!strcmp(type, "stsc") && bytes > len && !a->chunkinfo) { | |
171 | a->stsc = malloc(len - 12); | |
172 | if (a->stsc == NULL) { | |
173 | LOG_WARN("malloc fail"); | |
174 | return -1; | |
175 | } | |
176 | memcpy(a->stsc, streambuf->readp + 12, len - 12); | |
177 | } | |
178 | ||
179 | // build offsets table from stco and stored stsc | |
180 | if (!strcmp(type, "stco") && bytes > len && play == trak) { | |
181 | u32_t i; | |
182 | // extract chunk offsets | |
183 | u8_t *ptr = streambuf->readp + 12; | |
184 | u32_t entries = unpackN((u32_t *)ptr); | |
185 | ptr += 4; | |
186 | a->chunkinfo = malloc(sizeof(struct chunk_table) * (entries + 1)); | |
187 | if (a->chunkinfo == NULL) { | |
188 | LOG_WARN("malloc fail"); | |
189 | return -1; | |
190 | } | |
191 | for (i = 0; i < entries; ++i) { | |
192 | a->chunkinfo[i].offset = unpackN((u32_t *)ptr); | |
193 | a->chunkinfo[i].sample = 0; | |
194 | ptr += 4; | |
195 | } | |
196 | a->chunkinfo[i].sample = 0; | |
197 | a->chunkinfo[i].offset = 0; | |
198 | // fill in first sample id for each chunk from stored stsc | |
199 | if (a->stsc) { | |
200 | u32_t stsc_entries = unpackN((u32_t *)a->stsc); | |
201 | u32_t sample = 0; | |
202 | u32_t last = 0, last_samples = 0; | |
203 | u8_t *ptr = (u8_t *)a->stsc + 4; | |
204 | while (stsc_entries--) { | |
205 | u32_t first = unpackN((u32_t *)ptr); | |
206 | u32_t samples = unpackN((u32_t *)(ptr + 4)); | |
207 | if (last) { | |
208 | for (i = last - 1; i < first - 1; ++i) { | |
209 | a->chunkinfo[i].sample = sample; | |
210 | sample += last_samples; | |
211 | } | |
212 | } | |
213 | if (stsc_entries == 0) { | |
214 | for (i = first - 1; i < entries; ++i) { | |
215 | a->chunkinfo[i].sample = sample; | |
216 | sample += samples; | |
217 | } | |
218 | } | |
219 | last = first; | |
220 | last_samples = samples; | |
221 | ptr += 12; | |
222 | } | |
223 | free(a->stsc); | |
224 | a->stsc = NULL; | |
225 | } | |
226 | } | |
227 | ||
228 | // found media data, advance to start of first chunk and return | |
229 | if (!strcmp(type, "mdat")) { | |
230 | _buf_inc_readp(streambuf, 8); | |
231 | a->pos += 8; | |
232 | bytes -= 8; | |
233 | if (play) { | |
234 | LOG_DEBUG("type: mdat len: %u pos: %u", len, a->pos); | |
235 | if (a->chunkinfo && a->chunkinfo[0].offset > a->pos) { | |
236 | u32_t skip = a->chunkinfo[0].offset - a->pos; | |
237 | LOG_DEBUG("skipping: %u", skip); | |
238 | if (skip <= bytes) { | |
239 | _buf_inc_readp(streambuf, skip); | |
240 | a->pos += skip; | |
241 | } else { | |
242 | a->consume = skip; | |
243 | } | |
244 | } | |
245 | a->sample = a->nextchunk = 1; | |
246 | return 1; | |
247 | } else { | |
248 | LOG_DEBUG("type: mdat len: %u, no playable track found", len); | |
249 | return -1; | |
250 | } | |
251 | } | |
252 | ||
253 | // parse key-value atoms within ilst ---- entries to get encoder padding within iTunSMPB entry for gapless | |
254 | if (!strcmp(type, "----") && bytes > len) { | |
255 | u8_t *ptr = streambuf->readp + 8; | |
256 | u32_t remain = len - 8, size; | |
257 | if (!memcmp(ptr + 4, "mean", 4) && (size = unpackN((u32_t *)ptr)) < remain) { | |
258 | ptr += size; remain -= size; | |
259 | } | |
260 | if (!memcmp(ptr + 4, "name", 4) && (size = unpackN((u32_t *)ptr)) < remain && !memcmp(ptr + 12, "iTunSMPB", 8)) { | |
261 | ptr += size; remain -= size; | |
262 | } | |
263 | if (!memcmp(ptr + 4, "data", 4) && remain > 16 + 48) { | |
264 | // data is stored as hex strings: 0 start end samples | |
265 | u32_t b, c; u64_t d; | |
266 | if (sscanf((const char *)(ptr + 16), "%x %x %x " FMT_x64, &b, &b, &c, &d) == 4) { | |
267 | LOG_DEBUG("iTunSMPB start: %u end: %u samples: " FMT_u64, b, c, d); | |
268 | if (a->sttssamples && a->sttssamples < b + c + d) { | |
269 | LOG_DEBUG("reducing samples as stts count is less"); | |
270 | d = a->sttssamples - (b + c); | |
271 | } | |
272 | a->skip = b; | |
273 | a->samples = d; | |
274 | } | |
275 | } | |
276 | } | |
277 | ||
278 | // default to consuming entire box | |
279 | consume = len; | |
280 | ||
281 | // read into these boxes so reduce consume | |
282 | if (!strcmp(type, "moov") || !strcmp(type, "trak") || !strcmp(type, "mdia") || !strcmp(type, "minf") || !strcmp(type, "stbl") || | |
283 | !strcmp(type, "udta") || !strcmp(type, "ilst")) { | |
284 | consume = 8; | |
285 | } | |
286 | // special cases which mix mix data in the enclosing box which we want to read into | |
287 | if (!strcmp(type, "stsd")) consume = 16; | |
288 | if (!strcmp(type, "mp4a")) consume = 36; | |
289 | if (!strcmp(type, "meta")) consume = 12; | |
290 | ||
291 | // consume rest of box if it has been parsed (all in the buffer) or is not one we want to parse | |
292 | if (bytes >= consume) { | |
293 | LOG_DEBUG("type: %s len: %u consume: %u", type, len, consume); | |
294 | _buf_inc_readp(streambuf, consume); | |
295 | a->pos += consume; | |
296 | bytes -= consume; | |
297 | } else if ( !(!strcmp(type, "esds") || !strcmp(type, "stts") || !strcmp(type, "stsc") || | |
298 | !strcmp(type, "stco") || !strcmp(type, "----")) ) { | |
299 | LOG_DEBUG("type: %s len: %u consume: %u - partial consume: %u", type, len, consume, bytes); | |
300 | _buf_inc_readp(streambuf, bytes); | |
301 | a->pos += bytes; | |
302 | a->consume = consume - bytes; | |
303 | break; | |
304 | } else { | |
305 | break; | |
306 | } | |
307 | } | |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
312 | static decode_state faad_decode(void) { | |
313 | size_t bytes_total; | |
314 | size_t bytes_wrap; | |
315 | NeAACDecFrameInfo info; | |
316 | s32_t *iptr; | |
317 | bool endstream; | |
318 | frames_t frames; | |
319 | ||
320 | LOCK_S; | |
321 | bytes_total = _buf_used(streambuf); | |
322 | bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); | |
323 | ||
324 | if (stream.state <= DISCONNECT && !bytes_total) { | |
325 | UNLOCK_S; | |
326 | return DECODE_COMPLETE; | |
327 | } | |
328 | ||
329 | if (a->consume) { | |
330 | u32_t consume = min(a->consume, bytes_wrap); | |
331 | LOG_DEBUG("consume: %u of %u", consume, a->consume); | |
332 | _buf_inc_readp(streambuf, consume); | |
333 | a->pos += consume; | |
334 | a->consume -= consume; | |
335 | UNLOCK_S; | |
336 | return DECODE_RUNNING; | |
337 | } | |
338 | ||
339 | if (decode.new_stream) { | |
340 | int found = 0; | |
341 | static unsigned char channels; | |
342 | static unsigned long samplerate; | |
343 | ||
344 | if (a->type == '2') { | |
345 | ||
346 | // adts stream - seek for header | |
347 | while (bytes_wrap >= 2 && (*(streambuf->readp) != 0xFF || (*(streambuf->readp + 1) & 0xF6) != 0xF0)) { | |
348 | _buf_inc_readp(streambuf, 1); | |
349 | bytes_total--; | |
350 | bytes_wrap--; | |
351 | } | |
352 | ||
353 | if (bytes_wrap >= 2) { | |
354 | long n = NEAAC(a, Init, a->hAac, streambuf->readp, bytes_wrap, &samplerate, &channels); | |
355 | if (n < 0) { | |
356 | found = -1; | |
357 | } else { | |
358 | _buf_inc_readp(streambuf, n); | |
359 | found = 1; | |
360 | } | |
361 | } | |
362 | ||
363 | } else { | |
364 | ||
365 | // mp4 - read header | |
366 | found = read_mp4_header(&samplerate, &channels); | |
367 | } | |
368 | ||
369 | if (found == 1) { | |
370 | ||
371 | LOG_INFO("samplerate: %u channels: %u", samplerate, channels); | |
372 | bytes_total = _buf_used(streambuf); | |
373 | bytes_wrap = min(bytes_total, _buf_cont_read(streambuf)); | |
374 | ||
375 | LOCK_O; | |
376 | LOG_INFO("setting track_start"); | |
377 | output.next_sample_rate = decode_newstream(samplerate, output.supported_rates); | |
378 | IF_DSD( output.next_dop = false; ) | |
379 | output.track_start = outputbuf->writep; | |
380 | if (output.fade_mode) _checkfade(true); | |
381 | decode.new_stream = false; | |
382 | UNLOCK_O; | |
383 | ||
384 | } else if (found == -1) { | |
385 | ||
386 | LOG_WARN("error reading stream header"); | |
387 | UNLOCK_S; | |
388 | return DECODE_ERROR; | |
389 | ||
390 | } else { | |
391 | ||
392 | // not finished header parsing come back next time | |
393 | UNLOCK_S; | |
394 | return DECODE_RUNNING; | |
395 | } | |
396 | } | |
397 | ||
398 | if (bytes_wrap < WRAPBUF_LEN && bytes_total > WRAPBUF_LEN) { | |
399 | ||
400 | // make a local copy of frames which may have wrapped round the end of streambuf | |
401 | u8_t buf[WRAPBUF_LEN]; | |
402 | memcpy(buf, streambuf->readp, bytes_wrap); | |
403 | memcpy(buf + bytes_wrap, streambuf->buf, WRAPBUF_LEN - bytes_wrap); | |
404 | ||
405 | iptr = NEAAC(a, Decode, a->hAac, &info, buf, WRAPBUF_LEN); | |
406 | ||
407 | } else { | |
408 | ||
409 | iptr = NEAAC(a, Decode, a->hAac, &info, streambuf->readp, bytes_wrap); | |
410 | } | |
411 | ||
412 | if (info.error) { | |
413 | LOG_WARN("error: %u %s", info.error, NEAAC(a, GetErrorMessage, info.error)); | |
414 | } | |
415 | ||
416 | endstream = false; | |
417 | ||
418 | // mp4 end of chunk - skip to next offset | |
419 | if (a->chunkinfo && a->chunkinfo[a->nextchunk].offset && a->sample++ == a->chunkinfo[a->nextchunk].sample) { | |
420 | ||
421 | if (a->chunkinfo[a->nextchunk].offset > a->pos) { | |
422 | u32_t skip = a->chunkinfo[a->nextchunk].offset - a->pos; | |
423 | if (skip != info.bytesconsumed) { | |
424 | LOG_DEBUG("skipping to next chunk pos: %u consumed: %u != skip: %u", a->pos, info.bytesconsumed, skip); | |
425 | } | |
426 | if (bytes_total >= skip) { | |
427 | _buf_inc_readp(streambuf, skip); | |
428 | a->pos += skip; | |
429 | } else { | |
430 | a->consume = skip; | |
431 | } | |
432 | a->nextchunk++; | |
433 | } else { | |
434 | LOG_ERROR("error: need to skip backwards!"); | |
435 | endstream = true; | |
436 | } | |
437 | ||
438 | // adts and mp4 when not at end of chunk | |
439 | } else if (info.bytesconsumed != 0) { | |
440 | ||
441 | _buf_inc_readp(streambuf, info.bytesconsumed); | |
442 | a->pos += info.bytesconsumed; | |
443 | ||
444 | // error which doesn't advance streambuf - end | |
445 | } else { | |
446 | endstream = true; | |
447 | } | |
448 | ||
449 | UNLOCK_S; | |
450 | ||
451 | if (endstream) { | |
452 | LOG_WARN("unable to decode further"); | |
453 | return DECODE_ERROR; | |
454 | } | |
455 | ||
456 | if (!info.samples) { | |
457 | a->empty = true; | |
458 | return DECODE_RUNNING; | |
459 | } | |
460 | ||
461 | frames = info.samples / info.channels; | |
462 | ||
463 | if (a->skip) { | |
464 | u32_t skip; | |
465 | if (a->empty) { | |
466 | a->empty = false; | |
467 | a->skip -= frames; | |
468 | LOG_DEBUG("gapless: first frame empty, skipped %u frames at start", frames); | |
469 | } | |
470 | skip = min(frames, a->skip); | |
471 | LOG_DEBUG("gapless: skipping %u frames at start", skip); | |
472 | frames -= skip; | |
473 | a->skip -= skip; | |
474 | iptr += skip * info.channels; | |
475 | } | |
476 | ||
477 | if (a->samples) { | |
478 | if (a->samples < frames) { | |
479 | LOG_DEBUG("gapless: trimming %u frames from end", frames - a->samples); | |
480 | frames = (frames_t)a->samples; | |
481 | } | |
482 | a->samples -= frames; | |
483 | } | |
484 | ||
485 | LOG_SDEBUG("write %u frames", frames); | |
486 | ||
487 | LOCK_O_direct; | |
488 | ||
489 | while (frames > 0) { | |
490 | frames_t f; | |
491 | frames_t count; | |
492 | s32_t *optr; | |
493 | ||
494 | IF_DIRECT( | |
495 | f = _buf_cont_write(outputbuf) / BYTES_PER_FRAME; | |
496 | optr = (s32_t *)outputbuf->writep; | |
497 | ); | |
498 | IF_PROCESS( | |
499 | f = process.max_in_frames; | |
500 | optr = (s32_t *)process.inbuf; | |
501 | ); | |
502 | ||
503 | f = min(f, frames); | |
504 | count = f; | |
505 | ||
506 | if (info.channels == 2) { | |
507 | while (count--) { | |
508 | *optr++ = *iptr++ << 8; | |
509 | *optr++ = *iptr++ << 8; | |
510 | } | |
511 | } else if (info.channels == 1) { | |
512 | while (count--) { | |
513 | *optr++ = *iptr << 8; | |
514 | *optr++ = *iptr++ << 8; | |
515 | } | |
516 | } else { | |
517 | LOG_WARN("unsupported number of channels"); | |
518 | } | |
519 | ||
520 | frames -= f; | |
521 | ||
522 | IF_DIRECT( | |
523 | _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); | |
524 | ); | |
525 | IF_PROCESS( | |
526 | process.in_frames = f; | |
527 | if (frames) LOG_ERROR("unhandled case"); | |
528 | ); | |
529 | } | |
530 | ||
531 | UNLOCK_O_direct; | |
532 | ||
533 | return DECODE_RUNNING; | |
534 | } | |
535 | ||
536 | static void faad_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
537 | NeAACDecConfigurationPtr conf; | |
538 | ||
539 | LOG_INFO("opening %s stream", size == '2' ? "adts" : "mp4"); | |
540 | ||
541 | a->type = size; | |
542 | a->pos = a->consume = a->sample = a->nextchunk = 0; | |
543 | ||
544 | if (a->chunkinfo) { | |
545 | free(a->chunkinfo); | |
546 | } | |
547 | if (a->stsc) { | |
548 | free(a->stsc); | |
549 | } | |
550 | a->chunkinfo = NULL; | |
551 | a->stsc = NULL; | |
552 | a->skip = 0; | |
553 | a->samples = 0; | |
554 | a->sttssamples = 0; | |
555 | a->empty = false; | |
556 | ||
557 | if (a->hAac) { | |
558 | NEAAC(a, Close, a->hAac); | |
559 | } | |
560 | a->hAac = NEAAC(a, Open); | |
561 | ||
562 | conf = NEAAC(a, GetCurrentConfiguration, a->hAac); | |
563 | ||
564 | conf->outputFormat = FAAD_FMT_24BIT; | |
565 | conf->downMatrix = 1; | |
566 | ||
567 | if (!NEAAC(a, SetConfiguration, a->hAac, conf)) { | |
568 | LOG_WARN("error setting config"); | |
569 | }; | |
570 | } | |
571 | ||
572 | static void faad_close(void) { | |
573 | NEAAC(a, Close, a->hAac); | |
574 | a->hAac = NULL; | |
575 | if (a->chunkinfo) { | |
576 | free(a->chunkinfo); | |
577 | a->chunkinfo = NULL; | |
578 | } | |
579 | if (a->stsc) { | |
580 | free(a->stsc); | |
581 | a->stsc = NULL; | |
582 | } | |
583 | } | |
584 | ||
585 | static bool load_faad() { | |
586 | #if !LINKALL | |
587 | void *handle = dlopen(LIBFAAD, RTLD_NOW); | |
588 | char *err; | |
589 | ||
590 | if (!handle) { | |
591 | LOG_INFO("dlerror: %s", dlerror()); | |
592 | return false; | |
593 | } | |
594 | ||
595 | a->NeAACDecGetCurrentConfiguration = dlsym(handle, "NeAACDecGetCurrentConfiguration"); | |
596 | a->NeAACDecSetConfiguration = dlsym(handle, "NeAACDecSetConfiguration"); | |
597 | a->NeAACDecOpen = dlsym(handle, "NeAACDecOpen"); | |
598 | a->NeAACDecClose = dlsym(handle, "NeAACDecClose"); | |
599 | a->NeAACDecInit = dlsym(handle, "NeAACDecInit"); | |
600 | a->NeAACDecInit2 = dlsym(handle, "NeAACDecInit2"); | |
601 | a->NeAACDecDecode = dlsym(handle, "NeAACDecDecode"); | |
602 | a->NeAACDecGetErrorMessage = dlsym(handle, "NeAACDecGetErrorMessage"); | |
603 | ||
604 | if ((err = dlerror()) != NULL) { | |
605 | LOG_INFO("dlerror: %s", err); | |
606 | return false; | |
607 | } | |
608 | ||
609 | LOG_INFO("loaded "LIBFAAD""); | |
610 | #endif | |
611 | ||
612 | return true; | |
613 | } | |
614 | ||
615 | struct codec *register_faad(void) { | |
616 | static struct codec ret = { | |
617 | 'a', // id | |
618 | "aac", // types | |
619 | WRAPBUF_LEN, // min read | |
620 | 20480, // min space | |
621 | faad_open, // open | |
622 | faad_close, // close | |
623 | faad_decode, // decode | |
624 | }; | |
625 | ||
626 | a = malloc(sizeof(struct faad)); | |
627 | if (!a) { | |
628 | return NULL; | |
629 | } | |
630 | ||
631 | a->hAac = NULL; | |
632 | a->chunkinfo = NULL; | |
633 | a->stsc = NULL; | |
634 | ||
635 | if (!load_faad()) { | |
636 | return NULL; | |
637 | } | |
638 | ||
639 | LOG_INFO("using faad to decode aac"); | |
640 | return &ret; | |
641 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 "squeezelite.h" | |
21 | ||
22 | #if FFMPEG | |
23 | ||
24 | #include <libavformat/avformat.h> | |
25 | #include <libavcodec/avcodec.h> | |
26 | ||
27 | #define READ_SIZE 4096 * 4 // this is large enough to ensure ffmpeg always gets new data when decode is called | |
28 | #define WRITE_SIZE 256 * 1024 // FIXME - make smaller, but still to absorb max wma output | |
29 | ||
30 | // FIXME - do we need to align these params as per ffmpeg on i386? | |
31 | #define attribute_align_arg | |
32 | ||
33 | struct ff_s { | |
34 | // state for ffmpeg decoder | |
35 | bool wma; | |
36 | u8_t wma_mmsh; | |
37 | u8_t wma_playstream; | |
38 | u8_t wma_metadatastream; | |
39 | u8_t *readbuf; | |
40 | bool end_of_stream; | |
41 | AVInputFormat *input_format; | |
42 | AVFormatContext *formatC; | |
43 | AVCodecContext *codecC; | |
44 | AVFrame *frame; | |
45 | AVPacket *avpkt; | |
46 | unsigned mmsh_bytes_left; | |
47 | unsigned mmsh_bytes_pad; | |
48 | unsigned mmsh_packet_len; | |
49 | #if !LINKALL | |
50 | // ffmpeg symbols to be dynamically loaded from libavcodec | |
51 | unsigned (* avcodec_version)(void); | |
52 | AVCodec * (* avcodec_find_decoder)(int); | |
53 | int attribute_align_arg (* avcodec_open2)(AVCodecContext *, const AVCodec *, AVDictionary **); | |
54 | AVFrame * (* avcodec_alloc_frame)(void); | |
55 | void (* avcodec_free_frame)(AVFrame **); | |
56 | int attribute_align_arg (* avcodec_decode_audio4)(AVCodecContext *, AVFrame *, int *, const AVPacket *); | |
57 | // ffmpeg symbols to be dynamically loaded from libavformat | |
58 | unsigned (* avformat_version)(void); | |
59 | AVFormatContext * (* avformat_alloc_context)(void); | |
60 | void (* avformat_free_context)(AVFormatContext *); | |
61 | int (* avformat_open_input)(AVFormatContext **, const char *, AVInputFormat *, AVDictionary **); | |
62 | int (* avformat_find_stream_info)(AVFormatContext *, AVDictionary **); | |
63 | AVIOContext * (* avio_alloc_context)(unsigned char *, int, int, void *, | |
64 | int (*read_packet)(void *, uint8_t *, int), int (*write_packet)(void *, uint8_t *, int), int64_t (*seek)(void *, int64_t, int)); | |
65 | void (* av_init_packet)(AVPacket *); | |
66 | void (* av_free_packet)(AVPacket *); | |
67 | int (* av_read_frame)(AVFormatContext *, AVPacket *); | |
68 | AVInputFormat * (* av_find_input_format)(const char *); | |
69 | void (* av_register_all)(void); | |
70 | // ffmpeg symbols to be dynamically loaded from libavutil | |
71 | unsigned (* avutil_version)(void); | |
72 | void (* av_log_set_callback)(void (*)(void*, int, const char*, va_list)); | |
73 | void (* av_log_set_level)(int); | |
74 | int (* av_strerror)(int, char *, size_t); | |
75 | void * (* av_malloc)(size_t); | |
76 | void (* av_freep)(void *); | |
77 | #endif | |
78 | }; | |
79 | ||
80 | static struct ff_s *ff; | |
81 | ||
82 | extern log_level loglevel; | |
83 | ||
84 | extern struct buffer *streambuf; | |
85 | extern struct buffer *outputbuf; | |
86 | extern struct streamstate stream; | |
87 | extern struct outputstate output; | |
88 | extern struct decodestate decode; | |
89 | extern struct processstate process; | |
90 | ||
91 | #define LOCK_S mutex_lock(streambuf->mutex) | |
92 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
93 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
94 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
95 | #if PROCESS | |
96 | #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex) | |
97 | #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex) | |
98 | #define IF_DIRECT(x) if (decode.direct) { x } | |
99 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
100 | #else | |
101 | #define LOCK_O_direct mutex_lock(outputbuf->mutex) | |
102 | #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex) | |
103 | #define IF_DIRECT(x) { x } | |
104 | #define IF_PROCESS(x) | |
105 | #endif | |
106 | ||
107 | #if LINKALL | |
108 | #define AV(h, fn, ...) (av_ ## fn)(__VA_ARGS__) | |
109 | #define AVIO(h, fn, ...) (avio_ ## fn)(__VA_ARGS__) | |
110 | #define AVCODEC(h, fn, ...) (avcodec_ ## fn)(__VA_ARGS__) | |
111 | #define AVFORMAT(h, fn, ...) (avformat_ ## fn)(__VA_ARGS__) | |
112 | #else | |
113 | #define AV(h, fn, ...) (h)->av_##fn(__VA_ARGS__) | |
114 | #define AVIO(h, fn, ...) (h)->avio_##fn(__VA_ARGS__) | |
115 | #define AVCODEC(h, fn, ...) (h)->avcodec_##fn(__VA_ARGS__) | |
116 | #define AVFORMAT(h, fn, ...) (h)->avformat_##fn(__VA_ARGS__) | |
117 | #endif | |
118 | ||
119 | ||
120 | // our own version of useful error function not included in earlier ffmpeg versions | |
121 | static char *av__err2str(errnum) { | |
122 | static char buf[64]; | |
123 | AV(ff, strerror, errnum, buf, 64); | |
124 | return buf; | |
125 | } | |
126 | ||
127 | // parser to extract asf data packet length from asf header | |
128 | const u8_t header_guid[16] = { 0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C }; | |
129 | const u8_t file_props_guid[16] = { 0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 }; | |
130 | ||
131 | static int _parse_packlen(void) { | |
132 | int bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
133 | u8_t *ptr = streambuf->readp; | |
134 | int remain = 1; | |
135 | ||
136 | while (bytes >= 24 && remain > 0) { | |
137 | u32_t len = *(ptr+16) | *(ptr+17) << 8 | *(ptr+18) << 16 | *(ptr+19) << 24; // assume msb 32 bits are 0 | |
138 | if (!memcmp(ptr, header_guid, 16) && bytes >= 30) { | |
139 | ptr += 30; | |
140 | bytes -= 30; | |
141 | remain = len - 30; | |
142 | continue; | |
143 | } | |
144 | if (!memcmp(ptr, file_props_guid, 16) && len == 104) { | |
145 | u32_t packlen = *(ptr+92) | *(ptr+93) << 8 | *(ptr+94) << 16 | *(ptr+95) << 24; | |
146 | LOG_INFO("asf packet len: %u", packlen); | |
147 | return packlen; | |
148 | } | |
149 | ptr += len; | |
150 | bytes -= len; | |
151 | remain -= len; | |
152 | } | |
153 | ||
154 | LOG_WARN("could not parse packet length"); | |
155 | return 0; | |
156 | } | |
157 | ||
158 | static int _read_data(void *opaque, u8_t *buffer, int buf_size) { | |
159 | size_t bytes; | |
160 | ||
161 | LOCK_S; | |
162 | ||
163 | bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
164 | ff->end_of_stream = (stream.state <= DISCONNECT && bytes == 0); | |
165 | bytes = min(bytes, buf_size); | |
166 | ||
167 | // for chunked wma extract asf header and data frames from framing structure | |
168 | // pad asf data frames to size of packet extracted from asf header | |
169 | if (ff->wma_mmsh) { | |
170 | unsigned chunk_type = 0, chunk_len = 0; | |
171 | ||
172 | if (ff->mmsh_bytes_left) { | |
173 | // bytes remaining from previous frame | |
174 | if (bytes >= ff->mmsh_bytes_left) { | |
175 | bytes = ff->mmsh_bytes_left; | |
176 | ff->mmsh_bytes_left = 0; | |
177 | } else { | |
178 | ff->mmsh_bytes_left -= bytes; | |
179 | } | |
180 | } else if (ff->mmsh_bytes_pad) { | |
181 | // add padding for previous frame | |
182 | bytes = min(ff->mmsh_bytes_pad, buf_size); | |
183 | memset(buffer, 0, bytes); | |
184 | ff->mmsh_bytes_pad -= bytes; | |
185 | UNLOCK_S; | |
186 | return bytes; | |
187 | } else if (bytes >= 12) { | |
188 | // new chunk header | |
189 | chunk_type = (*(streambuf->readp) & 0x7f) | *(streambuf->readp + 1) << 8; | |
190 | chunk_len = *(streambuf->readp + 2) | *(streambuf->readp + 3) << 8; | |
191 | _buf_inc_readp(streambuf, 12); | |
192 | bytes -= 12; | |
193 | } else if (_buf_used(streambuf) >= 12) { | |
194 | // new chunk header split over end of streambuf, read in two | |
195 | u8_t header[12]; | |
196 | memcpy(header, streambuf->readp, bytes); | |
197 | _buf_inc_readp(streambuf, bytes); | |
198 | memcpy(header + bytes, streambuf->readp, 12 - bytes); | |
199 | _buf_inc_readp(streambuf, 12 - bytes); | |
200 | chunk_type = (header[0] & 0x7f) | header[1] << 8; | |
201 | chunk_len = header[2] | header[3] << 8; | |
202 | bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
203 | bytes = min(bytes, buf_size); | |
204 | } else { | |
205 | // should not get here... | |
206 | LOG_ERROR("chunk parser stalled bytes: %u %u", bytes, _buf_used(streambuf)); | |
207 | UNLOCK_S; | |
208 | return 0; | |
209 | } | |
210 | ||
211 | if (chunk_type && chunk_len) { | |
212 | if (chunk_type == 0x4824) { | |
213 | // asf header - parse packet length | |
214 | ff->mmsh_packet_len = _parse_packlen(); | |
215 | ff->mmsh_bytes_pad = 0; | |
216 | } else if (chunk_type == 0x4424 && ff->mmsh_packet_len) { | |
217 | // asf data packet - add padding | |
218 | ff->mmsh_bytes_pad = ff->mmsh_packet_len - chunk_len + 8; | |
219 | } else { | |
220 | LOG_INFO("unknown chunk: %04x", chunk_type); | |
221 | // other packet - no padding | |
222 | ff->mmsh_bytes_pad = 0; | |
223 | } | |
224 | ||
225 | if (chunk_len - 8 <= bytes) { | |
226 | bytes = chunk_len - 8; | |
227 | ff->mmsh_bytes_left = 0; | |
228 | } else { | |
229 | ff->mmsh_bytes_left = chunk_len - 8 - bytes; | |
230 | } | |
231 | } | |
232 | ||
233 | } | |
234 | ||
235 | memcpy(buffer, streambuf->readp, bytes); | |
236 | ||
237 | _buf_inc_readp(streambuf, bytes); | |
238 | ||
239 | if (ff->mmsh_bytes_pad && bytes + ff->mmsh_bytes_pad < buf_size) { | |
240 | memset(buffer + bytes, 0, ff->mmsh_bytes_pad); | |
241 | bytes += ff->mmsh_bytes_pad; | |
242 | ff->mmsh_bytes_pad = 0; | |
243 | } | |
244 | ||
245 | UNLOCK_S; | |
246 | ||
247 | return bytes; | |
248 | } | |
249 | ||
250 | static decode_state ff_decode(void) { | |
251 | int r, len, got_frame; | |
252 | AVPacket pkt_c; | |
253 | s32_t *optr = NULL; | |
254 | ||
255 | if (decode.new_stream) { | |
256 | ||
257 | AVIOContext *avio; | |
258 | AVStream *av_stream; | |
259 | AVCodec *codec; | |
260 | int o; | |
261 | int audio_stream = -1; | |
262 | ||
263 | ff->mmsh_bytes_left = ff->mmsh_bytes_pad = ff->mmsh_packet_len = 0; | |
264 | ||
265 | if (!ff->readbuf) { | |
266 | ff->readbuf = AV(ff, malloc, READ_SIZE + FF_INPUT_BUFFER_PADDING_SIZE); | |
267 | } | |
268 | ||
269 | avio = AVIO(ff, alloc_context, ff->readbuf, READ_SIZE, 0, NULL, _read_data, NULL, NULL); | |
270 | avio->seekable = 0; | |
271 | ||
272 | ff->formatC = AVFORMAT(ff, alloc_context); | |
273 | if (ff->formatC == NULL) { | |
274 | LOG_ERROR("null context"); | |
275 | return DECODE_ERROR; | |
276 | } | |
277 | ||
278 | ff->formatC->pb = avio; | |
279 | ff->formatC->flags |= AVFMT_FLAG_CUSTOM_IO | AVFMT_FLAG_NOPARSE; | |
280 | ||
281 | o = AVFORMAT(ff, open_input, &ff->formatC, "", ff->input_format, NULL); | |
282 | if (o < 0) { | |
283 | LOG_WARN("avformat_open_input: %d %s", o, av__err2str(o)); | |
284 | return DECODE_ERROR; | |
285 | } | |
286 | ||
287 | LOG_INFO("format: name:%s lname:%s", ff->formatC->iformat->name, ff->formatC->iformat->long_name); | |
288 | ||
289 | o = AVFORMAT(ff, find_stream_info, ff->formatC, NULL); | |
290 | if (o < 0) { | |
291 | LOG_WARN("avformat_find_stream_info: %d %s", o, av__err2str(o)); | |
292 | return DECODE_ERROR; | |
293 | } | |
294 | ||
295 | if (ff->wma && ff->wma_playstream < ff->formatC->nb_streams) { | |
296 | if (ff->formatC->streams[ff->wma_playstream]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { | |
297 | LOG_INFO("using wma stream sent from server: %i", ff->wma_playstream); | |
298 | audio_stream = ff->wma_playstream; | |
299 | } | |
300 | } | |
301 | ||
302 | if (audio_stream == -1) { | |
303 | int i; | |
304 | for (i = 0; i < ff->formatC->nb_streams; ++i) { | |
305 | if (ff->formatC->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { | |
306 | audio_stream = i; | |
307 | LOG_INFO("found stream: %i", i); | |
308 | break; | |
309 | } | |
310 | } | |
311 | } | |
312 | ||
313 | if (audio_stream == -1) { | |
314 | LOG_WARN("no audio stream found"); | |
315 | return DECODE_ERROR; | |
316 | } | |
317 | ||
318 | av_stream = ff->formatC->streams[audio_stream]; | |
319 | ||
320 | ff->codecC = av_stream->codec; | |
321 | ||
322 | codec = AVCODEC(ff, find_decoder, ff->codecC->codec_id); | |
323 | ||
324 | AVCODEC(ff, open2, ff->codecC, codec, NULL); | |
325 | ||
326 | ff->frame = AVCODEC(ff, alloc_frame); | |
327 | ||
328 | ff->avpkt = AV(ff, malloc, sizeof(AVPacket)); | |
329 | if (ff->avpkt == NULL) { | |
330 | LOG_ERROR("can't allocate avpkt"); | |
331 | return DECODE_ERROR; | |
332 | } | |
333 | ||
334 | AV(ff, init_packet, ff->avpkt); | |
335 | ff->avpkt->data = NULL; | |
336 | ff->avpkt->size = 0; | |
337 | ||
338 | LOCK_O; | |
339 | 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; ) | |
342 | output.track_start = outputbuf->writep; | |
343 | if (output.fade_mode) _checkfade(true); | |
344 | decode.new_stream = false; | |
345 | UNLOCK_O; | |
346 | } | |
347 | ||
348 | got_frame = 0; | |
349 | ||
350 | if ((r = AV(ff, read_frame, ff->formatC, ff->avpkt)) < 0) { | |
351 | if (r == AVERROR_EOF) { | |
352 | if (ff->end_of_stream) { | |
353 | LOG_INFO("decode complete"); | |
354 | return DECODE_COMPLETE; | |
355 | } else { | |
356 | LOG_INFO("codec end of file"); | |
357 | } | |
358 | } else { | |
359 | LOG_ERROR("av_read_frame error: %i %s", r, av__err2str(r)); | |
360 | } | |
361 | return DECODE_RUNNING; | |
362 | } | |
363 | ||
364 | // clone packet as we are adjusting it | |
365 | pkt_c = *ff->avpkt; | |
366 | ||
367 | IF_PROCESS( | |
368 | optr = (s32_t *)process.inbuf; | |
369 | process.in_frames = 0; | |
370 | ); | |
371 | ||
372 | while (pkt_c.size > 0 || got_frame) { | |
373 | ||
374 | len = AVCODEC(ff, decode_audio4, ff->codecC, ff->frame, &got_frame, &pkt_c); | |
375 | if (len < 0) { | |
376 | LOG_ERROR("avcodec_decode_audio4 error: %i %s", len, av__err2str(len)); | |
377 | return DECODE_RUNNING; | |
378 | } | |
379 | ||
380 | pkt_c.data += len; | |
381 | pkt_c.size -= len; | |
382 | ||
383 | if (got_frame) { | |
384 | ||
385 | s16_t *iptr16 = (s16_t *)ff->frame->data[0]; | |
386 | s32_t *iptr32 = (s32_t *)ff->frame->data[0]; | |
387 | s16_t *iptr16l = (s16_t *)ff->frame->data[0]; | |
388 | s16_t *iptr16r = (s16_t *)ff->frame->data[1]; | |
389 | s32_t *iptr32l = (s32_t *)ff->frame->data[0]; | |
390 | s32_t *iptr32r = (s32_t *)ff->frame->data[1]; | |
391 | float *iptrfl = (float *)ff->frame->data[0]; | |
392 | float *iptrfr = (float *)ff->frame->data[1]; | |
393 | ||
394 | frames_t frames = ff->frame->nb_samples; | |
395 | ||
396 | LOG_SDEBUG("got audio channels: %u samples: %u format: %u", ff->codecC->channels, ff->frame->nb_samples, | |
397 | ff->codecC->sample_fmt); | |
398 | ||
399 | LOCK_O_direct; | |
400 | ||
401 | while (frames > 0) { | |
402 | frames_t count; | |
403 | frames_t f; | |
404 | ||
405 | IF_DIRECT( | |
406 | optr = (s32_t *)outputbuf->writep; | |
407 | f = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; | |
408 | f = min(f, frames); | |
409 | ); | |
410 | ||
411 | IF_PROCESS( | |
412 | if (process.in_frames + frames > process.max_in_frames) { | |
413 | LOG_WARN("exceeded process buffer size - dropping frames"); | |
414 | break; | |
415 | } | |
416 | f = frames; | |
417 | ); | |
418 | ||
419 | count = f; | |
420 | ||
421 | if (ff->codecC->channels == 2) { | |
422 | if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S16) { | |
423 | while (count--) { | |
424 | *optr++ = *iptr16++ << 16; | |
425 | *optr++ = *iptr16++ << 16; | |
426 | } | |
427 | } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S32) { | |
428 | while (count--) { | |
429 | *optr++ = *iptr32++; | |
430 | *optr++ = *iptr32++; | |
431 | } | |
432 | } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S16P) { | |
433 | while (count--) { | |
434 | *optr++ = *iptr16l++ << 16; | |
435 | *optr++ = *iptr16r++ << 16; | |
436 | } | |
437 | } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S32P) { | |
438 | while (count--) { | |
439 | *optr++ = *iptr32l++; | |
440 | *optr++ = *iptr32r++; | |
441 | } | |
442 | } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_FLTP) { | |
443 | while (count--) { | |
444 | double scaledl = *iptrfl++ * 0x7fffffff; | |
445 | double scaledr = *iptrfr++ * 0x7fffffff; | |
446 | if (scaledl > 2147483647.0) scaledl = 2147483647.0; | |
447 | if (scaledl < -2147483648.0) scaledl = -2147483648.0; | |
448 | if (scaledr > 2147483647.0) scaledr = 2147483647.0; | |
449 | if (scaledr < -2147483648.0) scaledr = -2147483648.0; | |
450 | *optr++ = (s32_t)scaledl; | |
451 | *optr++ = (s32_t)scaledr; | |
452 | } | |
453 | } else { | |
454 | LOG_WARN("unsupported sample format: %u", ff->codecC->sample_fmt); | |
455 | } | |
456 | } else if (ff->codecC->channels == 1) { | |
457 | if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S16) { | |
458 | while (count--) { | |
459 | *optr++ = *iptr16 << 16; | |
460 | *optr++ = *iptr16++ << 16; | |
461 | } | |
462 | } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S32) { | |
463 | while (count--) { | |
464 | *optr++ = *iptr32; | |
465 | *optr++ = *iptr32++; | |
466 | } | |
467 | } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S16P) { | |
468 | while (count--) { | |
469 | *optr++ = *iptr16l << 16; | |
470 | *optr++ = *iptr16l++ << 16; | |
471 | } | |
472 | } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_S32P) { | |
473 | while (count--) { | |
474 | *optr++ = *iptr32l; | |
475 | *optr++ = *iptr32l++; | |
476 | } | |
477 | } else if (ff->codecC->sample_fmt == AV_SAMPLE_FMT_FLTP) { | |
478 | while (count--) { | |
479 | double scaled = *iptrfl++ * 0x7fffffff; | |
480 | if (scaled > 2147483647.0) scaled = 2147483647.0; | |
481 | if (scaled < -2147483648.0) scaled = -2147483648.0; | |
482 | *optr++ = (s32_t)scaled; | |
483 | *optr++ = (s32_t)scaled; | |
484 | } | |
485 | } else { | |
486 | LOG_WARN("unsupported sample format: %u", ff->codecC->sample_fmt); | |
487 | } | |
488 | } else { | |
489 | LOG_WARN("unsupported number of channels"); | |
490 | } | |
491 | ||
492 | frames -= f; | |
493 | ||
494 | IF_DIRECT( | |
495 | _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); | |
496 | ); | |
497 | ||
498 | IF_PROCESS( | |
499 | process.in_frames += f; | |
500 | ); | |
501 | } | |
502 | ||
503 | UNLOCK_O_direct; | |
504 | } | |
505 | } | |
506 | ||
507 | AV(ff, free_packet, ff->avpkt); | |
508 | ||
509 | return DECODE_RUNNING; | |
510 | } | |
511 | ||
512 | static void _free_ff_data(void) { | |
513 | if (ff->formatC) { | |
514 | if (ff->formatC->pb) AV(ff, freep, &ff->formatC->pb); | |
515 | AVFORMAT(ff, free_context, ff->formatC); | |
516 | ff->formatC = NULL; | |
517 | } | |
518 | ||
519 | if (ff->frame) { | |
520 | // ffmpeg version dependant free function | |
521 | #if !LINKALL | |
522 | ff->avcodec_free_frame ? AVCODEC(ff, free_frame, &ff->frame) : AV(ff, freep, &ff->frame); | |
523 | #elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54,28,0) | |
524 | AVCODEC(ff, free_frame, &ff->frame); | |
525 | #else | |
526 | AV(ff, freep, &ff->frame); | |
527 | #endif | |
528 | ff->frame = NULL; | |
529 | } | |
530 | ||
531 | if (ff->avpkt) { | |
532 | AV(ff, free_packet, ff->avpkt); | |
533 | AV(ff, freep, &ff->avpkt); | |
534 | ff->avpkt = NULL; | |
535 | } | |
536 | } | |
537 | ||
538 | static void ff_open_wma(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
539 | _free_ff_data(); | |
540 | ||
541 | ff->input_format = AV(ff, find_input_format, "asf"); | |
542 | if (ff->input_format == NULL) { | |
543 | LOG_ERROR("asf format not supported by ffmpeg library"); | |
544 | } | |
545 | ||
546 | ff->wma = true; | |
547 | ff->wma_mmsh = size - '0'; | |
548 | ff->wma_playstream = rate - 1; | |
549 | ff->wma_metadatastream = chan != '?' ? chan : 0; | |
550 | ||
551 | LOG_INFO("open wma chunking: %u playstream: %u metadatastream: %u", ff->wma_mmsh, ff->wma_playstream, ff->wma_metadatastream); | |
552 | } | |
553 | ||
554 | static void ff_open_alac(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
555 | _free_ff_data(); | |
556 | ||
557 | ff->input_format = AV(ff, find_input_format, "mp4"); | |
558 | if (ff->input_format == NULL) { | |
559 | LOG_ERROR("mp4 format not supported by ffmpeg library"); | |
560 | } | |
561 | ||
562 | ff->wma = false; | |
563 | ff->wma_mmsh = 0; | |
564 | ||
565 | LOG_INFO("open alac"); | |
566 | } | |
567 | ||
568 | static void ff_close(void) { | |
569 | _free_ff_data(); | |
570 | ||
571 | if (ff->readbuf) { | |
572 | AV(ff, freep, &ff->readbuf); | |
573 | ff->readbuf = NULL; | |
574 | } | |
575 | } | |
576 | ||
577 | static bool load_ff() { | |
578 | #if !LINKALL | |
579 | void *handle_codec = NULL, *handle_format = NULL, *handle_util = NULL; | |
580 | char name[30]; | |
581 | char *err; | |
582 | ||
583 | // we try to load the ffmpeg library version which matches the header file we are compiled with as structs differ between versions | |
584 | ||
585 | sprintf(name, LIBAVCODEC, LIBAVCODEC_VERSION_MAJOR); | |
586 | handle_codec = dlopen(name, RTLD_NOW); | |
587 | if (!handle_codec) { | |
588 | LOG_INFO("dlerror: %s", dlerror()); | |
589 | return false; | |
590 | } | |
591 | ||
592 | sprintf(name, LIBAVFORMAT, LIBAVFORMAT_VERSION_MAJOR); | |
593 | handle_format = dlopen(name, RTLD_NOW); | |
594 | if (!handle_format) { | |
595 | LOG_INFO("dlerror: %s", dlerror()); | |
596 | return false; | |
597 | } | |
598 | ||
599 | sprintf(name, LIBAVUTIL, LIBAVUTIL_VERSION_MAJOR); | |
600 | handle_util = dlopen(name, RTLD_NOW); | |
601 | if (!handle_util) { | |
602 | LOG_INFO("dlerror: %s", dlerror()); | |
603 | return false; | |
604 | } | |
605 | ||
606 | ff->avcodec_version = dlsym(handle_codec, "avcodec_version"); | |
607 | ff->avcodec_find_decoder = dlsym(handle_codec, "avcodec_find_decoder"); | |
608 | ff->avcodec_open2 = dlsym(handle_codec, "avcodec_open2"); | |
609 | ff->avcodec_alloc_frame = dlsym(handle_codec, "avcodec_alloc_frame"); | |
610 | ff->avcodec_free_frame = dlsym(handle_codec, "avcodec_free_frame"); | |
611 | ff->avcodec_decode_audio4 = dlsym(handle_codec, "avcodec_decode_audio4"); | |
612 | ff->av_init_packet = dlsym(handle_codec, "av_init_packet"); | |
613 | ff->av_free_packet = dlsym(handle_codec, "av_free_packet"); | |
614 | ||
615 | if ((err = dlerror()) != NULL) { | |
616 | LOG_INFO("dlerror: %s", err); | |
617 | return false; | |
618 | } | |
619 | ||
620 | LOG_INFO("loaded "LIBAVCODEC" (%u.%u.%u)", LIBAVCODEC_VERSION_MAJOR, ff->avcodec_version() >> 16, (ff->avcodec_version() >> 8) & 0xff, ff->avcodec_version() & 0xff); | |
621 | ||
622 | ff->avformat_version = dlsym(handle_format, "avformat_version"); | |
623 | ff->avformat_alloc_context = dlsym(handle_format, "avformat_alloc_context"); | |
624 | ff->avformat_free_context = dlsym(handle_format, "avformat_free_context"); | |
625 | ff->avformat_open_input = dlsym(handle_format, "avformat_open_input"); | |
626 | ff->avformat_find_stream_info = dlsym(handle_format, "avformat_find_stream_info"); | |
627 | ff->avio_alloc_context = dlsym(handle_format, "avio_alloc_context"); | |
628 | ff->av_read_frame = dlsym(handle_format, "av_read_frame"); | |
629 | ff->av_find_input_format= dlsym(handle_format, "av_find_input_format"); | |
630 | ff->av_register_all = dlsym(handle_format, "av_register_all"); | |
631 | ||
632 | if ((err = dlerror()) != NULL) { | |
633 | LOG_INFO("dlerror: %s", err); | |
634 | return false; | |
635 | } | |
636 | ||
637 | LOG_INFO("loaded "LIBAVFORMAT" (%u.%u.%u)", LIBAVFORMAT_VERSION_MAJOR, ff->avformat_version() >> 16, (ff->avformat_version() >> 8) & 0xff, ff->avformat_version() & 0xff); | |
638 | ||
639 | ff->avutil_version = dlsym(handle_util, "avutil_version"); | |
640 | ff->av_log_set_callback = dlsym(handle_util, "av_log_set_callback"); | |
641 | ff->av_log_set_level = dlsym(handle_util, "av_log_set_level"); | |
642 | ff->av_strerror = dlsym(handle_util, "av_strerror"); | |
643 | ff->av_malloc = dlsym(handle_util, "av_malloc"); | |
644 | ff->av_freep = dlsym(handle_util, "av_freep"); | |
645 | ||
646 | if ((err = dlerror()) != NULL) { | |
647 | LOG_INFO("dlerror: %s", err); | |
648 | return false; | |
649 | } | |
650 | ||
651 | LOG_INFO("loaded "LIBAVUTIL" (%u.%u.%u)", LIBAVUTIL_VERSION_MAJOR, ff->avutil_version() >> 16, (ff->avutil_version() >> 8) & 0xff, ff->avutil_version() & 0xff); | |
652 | ||
653 | #endif | |
654 | ||
655 | return true; | |
656 | } | |
657 | ||
658 | static int ff_log_level = 0; | |
659 | ||
660 | void av_err_callback(void *avcl, int level, const char *fmt, va_list vl) { | |
661 | if (level > ff_log_level) return; | |
662 | fprintf(stderr, "%s ffmpeg: ", logtime()); | |
663 | vfprintf(stderr, fmt, vl); | |
664 | fflush(stderr); | |
665 | } | |
666 | ||
667 | static bool registered = false; | |
668 | ||
669 | struct codec *register_ff(const char *codec) { | |
670 | if (!registered) { | |
671 | ||
672 | ff = malloc(sizeof(struct ff_s)); | |
673 | if (!ff) { | |
674 | return NULL; | |
675 | } | |
676 | ||
677 | memset(ff, 0, sizeof(struct ff_s)); | |
678 | ||
679 | if (!load_ff()) { | |
680 | return NULL; | |
681 | } | |
682 | ||
683 | switch (loglevel) { | |
684 | case lERROR: | |
685 | ff_log_level = AV_LOG_ERROR; break; | |
686 | case lWARN: | |
687 | ff_log_level = AV_LOG_WARNING; break; | |
688 | case lINFO: | |
689 | ff_log_level = AV_LOG_INFO; break; | |
690 | case lDEBUG: | |
691 | ff_log_level = AV_LOG_VERBOSE; break; | |
692 | default: break; | |
693 | } | |
694 | ||
695 | AV(ff, log_set_callback, av_err_callback); | |
696 | ||
697 | AV(ff, register_all); | |
698 | ||
699 | registered = true; | |
700 | } | |
701 | ||
702 | if (!strcmp(codec, "wma")) { | |
703 | ||
704 | static struct codec ret = { | |
705 | 'w', // id | |
706 | "wma,wmap,wmal", // types | |
707 | READ_SIZE, // min read | |
708 | WRITE_SIZE, // min space | |
709 | ff_open_wma, // open | |
710 | ff_close, // close | |
711 | ff_decode, // decode | |
712 | }; | |
713 | ||
714 | LOG_INFO("using ffmpeg to decode wma,wmap,wmal"); | |
715 | return &ret; | |
716 | } | |
717 | ||
718 | if (!strcmp(codec, "alc")) { | |
719 | ||
720 | static struct codec ret = { | |
721 | 'l', // id | |
722 | "alc", // types | |
723 | READ_SIZE, // min read | |
724 | WRITE_SIZE, // min space | |
725 | ff_open_alac,// open | |
726 | ff_close, // close | |
727 | ff_decode, // decode | |
728 | }; | |
729 | ||
730 | LOG_INFO("using ffmpeg to decode alc"); | |
731 | return &ret; | |
732 | } | |
733 | ||
734 | return NULL; | |
735 | } | |
736 | ||
737 | #endif |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezeplay emulator for linux | |
2 | * | |
3 | * (c) Adrian Smith 2012, triode1@btinternet.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 "squeezelite.h" | |
21 | ||
22 | #include <FLAC/stream_decoder.h> | |
23 | ||
24 | struct flac { | |
25 | FLAC__StreamDecoder *decoder; | |
26 | #if !LINKALL | |
27 | // FLAC symbols to be dynamically loaded | |
28 | const char **FLAC__StreamDecoderErrorStatusString; | |
29 | const char **FLAC__StreamDecoderStateString; | |
30 | FLAC__StreamDecoder * (* FLAC__stream_decoder_new)(void); | |
31 | FLAC__bool (* FLAC__stream_decoder_reset)(FLAC__StreamDecoder *decoder); | |
32 | void (* FLAC__stream_decoder_delete)(FLAC__StreamDecoder *decoder); | |
33 | FLAC__StreamDecoderInitStatus (* FLAC__stream_decoder_init_stream)( | |
34 | FLAC__StreamDecoder *decoder, | |
35 | FLAC__StreamDecoderReadCallback read_callback, | |
36 | FLAC__StreamDecoderSeekCallback seek_callback, | |
37 | FLAC__StreamDecoderTellCallback tell_callback, | |
38 | FLAC__StreamDecoderLengthCallback length_callback, | |
39 | FLAC__StreamDecoderEofCallback eof_callback, | |
40 | FLAC__StreamDecoderWriteCallback write_callback, | |
41 | FLAC__StreamDecoderMetadataCallback metadata_callback, | |
42 | FLAC__StreamDecoderErrorCallback error_callback, | |
43 | void *client_data | |
44 | ); | |
45 | FLAC__bool (* FLAC__stream_decoder_process_single)(FLAC__StreamDecoder *decoder); | |
46 | FLAC__StreamDecoderState (* FLAC__stream_decoder_get_state)(const FLAC__StreamDecoder *decoder); | |
47 | #endif | |
48 | }; | |
49 | ||
50 | static struct flac *f; | |
51 | ||
52 | extern log_level loglevel; | |
53 | ||
54 | extern struct buffer *streambuf; | |
55 | extern struct buffer *outputbuf; | |
56 | extern struct streamstate stream; | |
57 | extern struct outputstate output; | |
58 | extern struct decodestate decode; | |
59 | extern struct processstate process; | |
60 | ||
61 | #define LOCK_S mutex_lock(streambuf->mutex) | |
62 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
63 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
64 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
65 | #if PROCESS | |
66 | #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex) | |
67 | #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex) | |
68 | #define IF_DIRECT(x) if (decode.direct) { x } | |
69 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
70 | #else | |
71 | #define LOCK_O_direct mutex_lock(outputbuf->mutex) | |
72 | #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex) | |
73 | #define IF_DIRECT(x) { x } | |
74 | #define IF_PROCESS(x) | |
75 | #endif | |
76 | ||
77 | #if LINKALL | |
78 | #define FLAC(h, fn, ...) (FLAC__ ## fn)(__VA_ARGS__) | |
79 | #define FLAC_A(h, a) (FLAC__ ## a) | |
80 | #else | |
81 | #define FLAC(h, fn, ...) (h)->FLAC__##fn(__VA_ARGS__) | |
82 | #define FLAC_A(h, a) (h)->FLAC__ ## a | |
83 | #endif | |
84 | ||
85 | static FLAC__StreamDecoderReadStatus read_cb(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *want, void *client_data) { | |
86 | size_t bytes; | |
87 | bool end; | |
88 | ||
89 | LOCK_S; | |
90 | bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
91 | bytes = min(bytes, *want); | |
92 | end = (stream.state <= DISCONNECT && bytes == 0); | |
93 | ||
94 | memcpy(buffer, streambuf->readp, bytes); | |
95 | _buf_inc_readp(streambuf, bytes); | |
96 | UNLOCK_S; | |
97 | ||
98 | *want = bytes; | |
99 | ||
100 | return end ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; | |
101 | } | |
102 | ||
103 | static FLAC__StreamDecoderWriteStatus write_cb(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, | |
104 | const FLAC__int32 *const buffer[], void *client_data) { | |
105 | ||
106 | size_t frames = frame->header.blocksize; | |
107 | unsigned bits_per_sample = frame->header.bits_per_sample; | |
108 | unsigned channels = frame->header.channels; | |
109 | ||
110 | FLAC__int32 *lptr = (FLAC__int32 *)buffer[0]; | |
111 | FLAC__int32 *rptr = (FLAC__int32 *)buffer[channels > 1 ? 1 : 0]; | |
112 | ||
113 | if (decode.new_stream) { | |
114 | LOCK_O; | |
115 | LOG_INFO("setting track_start"); | |
116 | output.track_start = outputbuf->writep; | |
117 | decode.new_stream = false; | |
118 | ||
119 | #if DSD | |
120 | if (output.has_dop && bits_per_sample == 24 && is_flac_dop((u32_t *)lptr, (u32_t *)rptr, frames)) { | |
121 | LOG_INFO("file contains DOP"); | |
122 | output.next_dop = true; | |
123 | output.next_sample_rate = frame->header.sample_rate; | |
124 | output.fade = FADE_INACTIVE; | |
125 | } else { | |
126 | output.next_sample_rate = decode_newstream(frame->header.sample_rate, output.supported_rates); | |
127 | output.next_dop = false; | |
128 | if (output.fade_mode) _checkfade(true); | |
129 | } | |
130 | #else | |
131 | output.next_sample_rate = decode_newstream(frame->header.sample_rate, output.supported_rates); | |
132 | if (output.fade_mode) _checkfade(true); | |
133 | #endif | |
134 | ||
135 | UNLOCK_O; | |
136 | } | |
137 | ||
138 | LOCK_O_direct; | |
139 | ||
140 | while (frames > 0) { | |
141 | frames_t f; | |
142 | frames_t count; | |
143 | s32_t *optr; | |
144 | ||
145 | IF_DIRECT( | |
146 | optr = (s32_t *)outputbuf->writep; | |
147 | f = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; | |
148 | ); | |
149 | IF_PROCESS( | |
150 | optr = (s32_t *)process.inbuf; | |
151 | f = process.max_in_frames; | |
152 | ); | |
153 | ||
154 | f = min(f, frames); | |
155 | ||
156 | count = f; | |
157 | ||
158 | if (bits_per_sample == 8) { | |
159 | while (count--) { | |
160 | *optr++ = *lptr++ << 24; | |
161 | *optr++ = *rptr++ << 24; | |
162 | } | |
163 | } else if (bits_per_sample == 16) { | |
164 | while (count--) { | |
165 | *optr++ = *lptr++ << 16; | |
166 | *optr++ = *rptr++ << 16; | |
167 | } | |
168 | } else if (bits_per_sample == 24) { | |
169 | while (count--) { | |
170 | *optr++ = *lptr++ << 8; | |
171 | *optr++ = *rptr++ << 8; | |
172 | } | |
173 | } else if (bits_per_sample == 32) { | |
174 | while (count--) { | |
175 | *optr++ = *lptr++; | |
176 | *optr++ = *rptr++; | |
177 | } | |
178 | } else { | |
179 | LOG_ERROR("unsupported bits per sample: %u", bits_per_sample); | |
180 | } | |
181 | ||
182 | frames -= f; | |
183 | ||
184 | IF_DIRECT( | |
185 | _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); | |
186 | ); | |
187 | IF_PROCESS( | |
188 | process.in_frames = f; | |
189 | if (frames) LOG_ERROR("unhandled case"); | |
190 | ); | |
191 | } | |
192 | ||
193 | UNLOCK_O_direct; | |
194 | ||
195 | return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; | |
196 | } | |
197 | ||
198 | static void error_cb(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { | |
199 | LOG_INFO("flac error: %s", FLAC_A(f, StreamDecoderErrorStatusString)[status]); | |
200 | } | |
201 | ||
202 | static void flac_open(u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness) { | |
203 | if (f->decoder) { | |
204 | FLAC(f, stream_decoder_reset, f->decoder); | |
205 | } else { | |
206 | f->decoder = FLAC(f, stream_decoder_new); | |
207 | } | |
208 | FLAC(f, stream_decoder_init_stream, f->decoder, &read_cb, NULL, NULL, NULL, NULL, &write_cb, NULL, &error_cb, NULL); | |
209 | } | |
210 | ||
211 | static void flac_close(void) { | |
212 | FLAC(f, stream_decoder_delete, f->decoder); | |
213 | f->decoder = NULL; | |
214 | } | |
215 | ||
216 | static decode_state flac_decode(void) { | |
217 | bool ok = FLAC(f, stream_decoder_process_single, f->decoder); | |
218 | FLAC__StreamDecoderState state = FLAC(f, stream_decoder_get_state, f->decoder); | |
219 | ||
220 | if (!ok && state != FLAC__STREAM_DECODER_END_OF_STREAM) { | |
221 | LOG_INFO("flac error: %s", FLAC_A(f, StreamDecoderStateString)[state]); | |
222 | }; | |
223 | ||
224 | if (state == FLAC__STREAM_DECODER_END_OF_STREAM) { | |
225 | return DECODE_COMPLETE; | |
226 | } else if (state > FLAC__STREAM_DECODER_END_OF_STREAM) { | |
227 | return DECODE_ERROR; | |
228 | } else { | |
229 | return DECODE_RUNNING; | |
230 | } | |
231 | } | |
232 | ||
233 | static bool load_flac() { | |
234 | #if !LINKALL | |
235 | void *handle = dlopen(LIBFLAC, RTLD_NOW); | |
236 | char *err; | |
237 | ||
238 | if (!handle) { | |
239 | LOG_INFO("dlerror: %s", dlerror()); | |
240 | return false; | |
241 | } | |
242 | ||
243 | f->FLAC__StreamDecoderErrorStatusString = dlsym(handle, "FLAC__StreamDecoderErrorStatusString"); | |
244 | f->FLAC__StreamDecoderStateString = dlsym(handle, "FLAC__StreamDecoderStateString"); | |
245 | f->FLAC__stream_decoder_new = dlsym(handle, "FLAC__stream_decoder_new"); | |
246 | f->FLAC__stream_decoder_reset = dlsym(handle, "FLAC__stream_decoder_reset"); | |
247 | f->FLAC__stream_decoder_delete = dlsym(handle, "FLAC__stream_decoder_delete"); | |
248 | f->FLAC__stream_decoder_init_stream = dlsym(handle, "FLAC__stream_decoder_init_stream"); | |
249 | f->FLAC__stream_decoder_process_single = dlsym(handle, "FLAC__stream_decoder_process_single"); | |
250 | f->FLAC__stream_decoder_get_state = dlsym(handle, "FLAC__stream_decoder_get_state"); | |
251 | ||
252 | if ((err = dlerror()) != NULL) { | |
253 | LOG_INFO("dlerror: %s", err); | |
254 | return false; | |
255 | } | |
256 | ||
257 | LOG_INFO("loaded "LIBFLAC); | |
258 | #endif | |
259 | ||
260 | return true; | |
261 | } | |
262 | ||
263 | struct codec *register_flac(void) { | |
264 | static struct codec ret = { | |
265 | 'f', // id | |
266 | "flc", // types | |
267 | 8192, // min read | |
268 | 102400, // min space | |
269 | flac_open, // open | |
270 | flac_close, // close | |
271 | flac_decode, // decode | |
272 | }; | |
273 | ||
274 | f = malloc(sizeof(struct flac)); | |
275 | if (!f) { | |
276 | return NULL; | |
277 | } | |
278 | ||
279 | f->decoder = NULL; | |
280 | ||
281 | if (!load_flac()) { | |
282 | return NULL; | |
283 | } | |
284 | ||
285 | LOG_INFO("using flac to decode flc"); | |
286 | return &ret; | |
287 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // ir thread - linux only | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #if IR | |
25 | ||
26 | #include <lirc/lirc_client.h> | |
27 | ||
28 | #define LIRC_CLIENT_ID "squeezelite" | |
29 | ||
30 | static log_level loglevel; | |
31 | ||
32 | struct irstate ir; | |
33 | ||
34 | static struct lirc_config *config = NULL; | |
35 | static sockfd fd = -1; | |
36 | ||
37 | static thread_type thread; | |
38 | ||
39 | #define LOCK_I mutex_lock(ir.mutex) | |
40 | #define UNLOCK_I mutex_unlock(ir.mutex) | |
41 | ||
42 | #if !LINKALL | |
43 | struct lirc { | |
44 | // LIRC symbols to be dynamically loaded | |
45 | int (* lirc_init)(char *prog, int verbose); | |
46 | int (* lirc_deinit)(void); | |
47 | int (* lirc_readconfig)(char *file, struct lirc_config **config, int (check) (char *s)); | |
48 | void (* lirc_freeconfig)(struct lirc_config *config); | |
49 | int (* lirc_nextcode)(char **code); | |
50 | int (* lirc_code2char)(struct lirc_config *config, char *code, char **string); | |
51 | }; | |
52 | ||
53 | static struct lirc *i; | |
54 | #endif | |
55 | ||
56 | #if LINKALL | |
57 | #define LIRC(h, fn, ...) (lirc_ ## fn)(__VA_ARGS__) | |
58 | #else | |
59 | #define LIRC(h, fn, ...) (h)->lirc_##fn(__VA_ARGS__) | |
60 | #endif | |
61 | ||
62 | // cmds based on entires in Slim_Device_Remote.ir | |
63 | // these may appear as config entries in .lircrc files | |
64 | static struct { | |
65 | char *cmd; | |
66 | u32_t code; | |
67 | } cmdmap[] = { | |
68 | { "voldown", 0x768900ff }, | |
69 | { "volup", 0x7689807f }, | |
70 | { "rew", 0x7689c03f }, | |
71 | { "fwd", 0x7689a05f }, | |
72 | { "pause", 0x768920df }, | |
73 | { "play", 0x768910ef }, | |
74 | { "power", 0x768940bf }, | |
75 | { "muting", 0x7689c43b }, | |
76 | { "power_on", 0x76898f70 }, | |
77 | { "power_off",0x76898778 }, | |
78 | { NULL, 0 }, | |
79 | }; | |
80 | ||
81 | // selected lirc namespace button names as defaults - some support repeat | |
82 | static struct { | |
83 | char *lirc; | |
84 | u32_t code; | |
85 | bool repeat; | |
86 | } keymap[] = { | |
87 | { "KEY_VOLUMEDOWN", 0x768900ff, true }, | |
88 | { "KEY_VOLUMEUP", 0x7689807f, true }, | |
89 | { "KEY_PREVIOUS", 0x7689c03f, false }, | |
90 | { "KEY_REWIND", 0x7689c03f, false }, | |
91 | { "KEY_NEXT", 0x7689a05f, false }, | |
92 | { "KEY_FORWARD", 0x7689a05f, false }, | |
93 | { "KEY_PAUSE", 0x768920df, true }, | |
94 | { "KEY_PLAY", 0x768910ef, false }, | |
95 | { "KEY_POWER", 0x768940bf, false }, | |
96 | { "KEY_MUTE", 0x7689c43b, false }, | |
97 | { NULL, 0 , false }, | |
98 | }; | |
99 | ||
100 | static u32_t ir_cmd_map(const char *c) { | |
101 | int i; | |
102 | for (i = 0; cmdmap[i].cmd; i++) { | |
103 | if (!strcmp(c, cmdmap[i].cmd)) { | |
104 | return cmdmap[i].code; | |
105 | } | |
106 | } | |
107 | return 0; | |
108 | } | |
109 | ||
110 | static u32_t ir_key_map(const char *c, const char *r) { | |
111 | int i; | |
112 | for (i = 0; keymap[i].lirc; i++) { | |
113 | if (!strcmp(c, keymap[i].lirc)) { | |
114 | if (keymap[i].repeat || !strcmp(r, "00")) { | |
115 | return keymap[i].code; | |
116 | } | |
117 | LOG_DEBUG("repeat suppressed"); | |
118 | break; | |
119 | } | |
120 | } | |
121 | return 0; | |
122 | } | |
123 | ||
124 | static void *ir_thread() { | |
125 | char *code; | |
126 | ||
127 | while (fd > 0 && LIRC(i, nextcode, &code) == 0) { | |
128 | ||
129 | u32_t now = gettime_ms(); | |
130 | u32_t ir_code = 0; | |
131 | ||
132 | if (code == NULL) continue; | |
133 | ||
134 | if (config) { | |
135 | // allow lirc_client to decode then lookup cmd in our table | |
136 | // we can only send one IR event to slimproto so break after first one | |
137 | char *c; | |
138 | while (LIRC(i, code2char, config, code, &c) == 0 && c != NULL) { | |
139 | ir_code = ir_cmd_map(c); | |
140 | if (ir_code) { | |
141 | LOG_DEBUG("ir cmd: %s -> %x", c, ir_code); | |
142 | } | |
143 | } | |
144 | } | |
145 | ||
146 | if (!ir_code) { | |
147 | // try to match on lirc button name if it is from the standard namespace | |
148 | // this allows use of non slim remotes without a specific entry in .lircrc | |
149 | char *b, *r; | |
150 | strtok(code, " \n"); // discard | |
151 | r = strtok(NULL, " \n"); // repeat count | |
152 | b = strtok(NULL, " \n"); // key name | |
153 | if (r && b) { | |
154 | ir_code = ir_key_map(b, r); | |
155 | LOG_DEBUG("ir lirc: %s [%s] -> %x", b, r, ir_code); | |
156 | } | |
157 | } | |
158 | ||
159 | if (ir_code) { | |
160 | LOCK_I; | |
161 | if (ir.code) { | |
162 | LOG_DEBUG("code dropped"); | |
163 | } | |
164 | ir.code = ir_code; | |
165 | ir.ts = now; | |
166 | UNLOCK_I; | |
167 | wake_controller(); | |
168 | } | |
169 | ||
170 | free(code); | |
171 | } | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | #if !LINKALL | |
177 | static bool load_lirc() { | |
178 | void *handle = dlopen(LIBLIRC, RTLD_NOW); | |
179 | char *err; | |
180 | ||
181 | if (!handle) { | |
182 | LOG_INFO("dlerror: %s", dlerror()); | |
183 | return false; | |
184 | } | |
185 | ||
186 | i->lirc_init = dlsym(handle, "lirc_init"); | |
187 | i->lirc_deinit = dlsym(handle, "lirc_deinit"); | |
188 | i->lirc_readconfig = dlsym(handle, "lirc_readconfig"); | |
189 | i->lirc_freeconfig = dlsym(handle, "lirc_freeconfig"); | |
190 | i->lirc_nextcode = dlsym(handle, "lirc_nextcode"); | |
191 | i->lirc_code2char = dlsym(handle, "lirc_code2char"); | |
192 | ||
193 | if ((err = dlerror()) != NULL) { | |
194 | LOG_INFO("dlerror: %s", err); | |
195 | return false; | |
196 | } | |
197 | ||
198 | LOG_INFO("loaded "LIBLIRC); | |
199 | return true; | |
200 | } | |
201 | #endif | |
202 | ||
203 | void ir_init(log_level level, char *lircrc) { | |
204 | loglevel = level; | |
205 | ||
206 | #if !LINKALL | |
207 | i = malloc(sizeof(struct lirc)); | |
208 | if (!i || !load_lirc()) { | |
209 | return; | |
210 | } | |
211 | #endif | |
212 | ||
213 | fd = LIRC(i, init, LIRC_CLIENT_ID, 0); | |
214 | ||
215 | if (fd > 0) { | |
216 | if (LIRC(i, readconfig,lircrc, &config, NULL) != 0) { | |
217 | LOG_WARN("error reading config: %s", lircrc); | |
218 | } | |
219 | ||
220 | mutex_create(ir.mutex); | |
221 | ||
222 | pthread_attr_t attr; | |
223 | pthread_attr_init(&attr); | |
224 | pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + IR_THREAD_STACK_SIZE); | |
225 | pthread_create(&thread, &attr, ir_thread, NULL); | |
226 | pthread_attr_destroy(&attr); | |
227 | ||
228 | } else { | |
229 | LOG_WARN("failed to connect to lircd - ir processing disabled"); | |
230 | } | |
231 | } | |
232 | ||
233 | void ir_close(void) { | |
234 | if (fd > 0) { | |
235 | fd = -1; | |
236 | if (config) { | |
237 | LIRC(i, freeconfig, config); | |
238 | } | |
239 | LIRC(i, deinit); | |
240 | ||
241 | pthread_cancel(thread); | |
242 | pthread_join(thread, NULL); | |
243 | mutex_destroy(ir.mutex); | |
244 | } | |
245 | } | |
246 | ||
247 | #endif //#if IR |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 "squeezelite.h" | |
21 | ||
22 | #include <mad.h> | |
23 | ||
24 | #define MAD_DELAY 529 | |
25 | ||
26 | #define READBUF_SIZE 2048 // local buffer used by decoder: FIXME merge with any other decoders needing one? | |
27 | ||
28 | struct mad { | |
29 | u8_t *readbuf; | |
30 | unsigned readbuf_len; | |
31 | struct mad_stream stream; | |
32 | struct mad_frame frame; | |
33 | struct mad_synth synth; | |
34 | enum mad_error last_error; | |
35 | // for lame gapless processing | |
36 | int checktags; | |
37 | u32_t consume; | |
38 | u32_t skip; | |
39 | u64_t samples; | |
40 | u32_t padding; | |
41 | #if !LINKALL | |
42 | // mad symbols to be dynamically loaded | |
43 | void (* mad_stream_init)(struct mad_stream *); | |
44 | void (* mad_frame_init)(struct mad_frame *); | |
45 | void (* mad_synth_init)(struct mad_synth *); | |
46 | void (* mad_frame_finish)(struct mad_frame *); | |
47 | void (* mad_stream_finish)(struct mad_stream *); | |
48 | void (* mad_stream_buffer)(struct mad_stream *, unsigned char const *, unsigned long); | |
49 | int (* mad_frame_decode)(struct mad_frame *, struct mad_stream *); | |
50 | void (* mad_synth_frame)(struct mad_synth *, struct mad_frame const *); | |
51 | char const *(* mad_stream_errorstr)(struct mad_stream const *); | |
52 | #endif | |
53 | }; | |
54 | ||
55 | static struct mad *m; | |
56 | ||
57 | extern log_level loglevel; | |
58 | ||
59 | extern struct buffer *streambuf; | |
60 | extern struct buffer *outputbuf; | |
61 | extern struct streamstate stream; | |
62 | extern struct outputstate output; | |
63 | extern struct decodestate decode; | |
64 | extern struct processstate process; | |
65 | ||
66 | #define LOCK_S mutex_lock(streambuf->mutex) | |
67 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
68 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
69 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
70 | #if PROCESS | |
71 | #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex) | |
72 | #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex) | |
73 | #define IF_DIRECT(x) if (decode.direct) { x } | |
74 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
75 | #else | |
76 | #define LOCK_O_direct mutex_lock(outputbuf->mutex) | |
77 | #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex) | |
78 | #define IF_DIRECT(x) { x } | |
79 | #define IF_PROCESS(x) | |
80 | #endif | |
81 | ||
82 | #if LINKALL | |
83 | #define MAD(h, fn, ...) (mad_ ## fn)(__VA_ARGS__) | |
84 | #else | |
85 | #define MAD(h, fn, ...) (h)->mad_##fn(__VA_ARGS__) | |
86 | #endif | |
87 | ||
88 | // based on libmad minimad.c scale | |
89 | static inline u32_t scale(mad_fixed_t sample) { | |
90 | sample += (1L << (MAD_F_FRACBITS - 24)); | |
91 | ||
92 | if (sample >= MAD_F_ONE) | |
93 | sample = MAD_F_ONE - 1; | |
94 | else if (sample < -MAD_F_ONE) | |
95 | sample = -MAD_F_ONE; | |
96 | ||
97 | return (s32_t)(sample >> (MAD_F_FRACBITS + 1 - 24)) << 8; | |
98 | } | |
99 | ||
100 | // check for id3.2 tag at start of file - http://id3.org/id3v2.4.0-structure, return length | |
101 | static unsigned _check_id3_tag(size_t bytes) { | |
102 | u8_t *ptr = streambuf->readp; | |
103 | u32_t size = 0; | |
104 | ||
105 | if (bytes > 10 && *ptr == 'I' && *(ptr+1) == 'D' && *(ptr+2) == '3') { | |
106 | // size is encoded as syncsafe integer, add 10 if footer present | |
107 | if (*(ptr+6) < 0x80 && *(ptr+7) < 0x80 && *(ptr+8) < 0x80 && *(ptr+9) < 0x80) { | |
108 | size = 10 + (*(ptr+6) << 21) + (*(ptr+7) << 14) + (*(ptr+8) << 7) + *(ptr+9) + ((*(ptr+5) & 0x10) ? 10 : 0); | |
109 | LOG_DEBUG("id3.2 tag len: %u", size); | |
110 | } | |
111 | } | |
112 | ||
113 | return size; | |
114 | } | |
115 | ||
116 | // check for lame gapless params, don't advance streambuf | |
117 | static void _check_lame_header(size_t bytes) { | |
118 | u8_t *ptr = streambuf->readp; | |
119 | ||
120 | if (*ptr == 0xff && (*(ptr+1) & 0xf0) == 0xf0 && bytes > 180) { | |
121 | ||
122 | u32_t frame_count = 0, enc_delay = 0, enc_padding = 0; | |
123 | u8_t flags; | |
124 | ||
125 | // 2 channels | |
126 | if (!memcmp(ptr + 36, "Xing", 4) || !memcmp(ptr + 36, "Info", 4)) { | |
127 | ptr += 36 + 7; | |
128 | // mono | |
129 | } else if (!memcmp(ptr + 21, "Xing", 4) || !memcmp(ptr + 21, "Info", 4)) { | |
130 | ptr += 21 + 7; | |
131 | } | |
132 | ||
133 | flags = *ptr; | |
134 | ||
135 | if (flags & 0x01) { | |
136 | frame_count = unpackN((u32_t *)(ptr + 1)); | |
137 | ptr += 4; | |
138 | } | |
139 | if (flags & 0x02) ptr += 4; | |
140 | if (flags & 0x04) ptr += 100; | |
141 | if (flags & 0x08) ptr += 4; | |
142 | ||
143 | if (!!memcmp(ptr+1, "LAME", 4)) { | |
144 | return; | |
145 | } | |
146 | ||
147 | ptr += 22; | |
148 | ||
149 | enc_delay = (*ptr << 4 | *(ptr + 1) >> 4) + MAD_DELAY; | |
150 | enc_padding = (*(ptr + 1) & 0xF) << 8 | *(ptr + 2); | |
151 | enc_padding = enc_padding > MAD_DELAY ? enc_padding - MAD_DELAY : 0; | |
152 | ||
153 | // add one frame to initial skip for this (empty) frame | |
154 | m->skip = enc_delay + 1152; | |
155 | m->samples = frame_count * 1152 - enc_delay - enc_padding; | |
156 | m->padding = enc_padding; | |
157 | ||
158 | LOG_INFO("gapless: skip: %u samples: " FMT_u64 " delay: %u padding: %u", m->skip, m->samples, enc_delay, enc_padding); | |
159 | } | |
160 | } | |
161 | ||
162 | static decode_state mad_decode(void) { | |
163 | size_t bytes; | |
164 | bool eos = false; | |
165 | ||
166 | LOCK_S; | |
167 | bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
168 | ||
169 | if (m->checktags) { | |
170 | if (m->checktags == 1) { | |
171 | m->consume = _check_id3_tag(bytes); | |
172 | m->checktags = 2; | |
173 | } | |
174 | if (m->consume) { | |
175 | u32_t consume = min(m->consume, bytes); | |
176 | LOG_DEBUG("consume: %u of %u", consume, m->consume); | |
177 | _buf_inc_readp(streambuf, consume); | |
178 | m->consume -= consume; | |
179 | UNLOCK_S; | |
180 | return DECODE_RUNNING; | |
181 | } | |
182 | if (m->checktags == 2) { | |
183 | if (!stream.meta_interval) { | |
184 | _check_lame_header(bytes); | |
185 | } | |
186 | m->checktags = 0; | |
187 | } | |
188 | } | |
189 | ||
190 | if (m->stream.next_frame && m->readbuf_len) { | |
191 | m->readbuf_len -= m->stream.next_frame - m->readbuf; | |
192 | memmove(m->readbuf, m->stream.next_frame, m->readbuf_len); | |
193 | } | |
194 | ||
195 | bytes = min(bytes, READBUF_SIZE - m->readbuf_len); | |
196 | memcpy(m->readbuf + m->readbuf_len, streambuf->readp, bytes); | |
197 | m->readbuf_len += bytes; | |
198 | _buf_inc_readp(streambuf, bytes); | |
199 | ||
200 | if (stream.state <= DISCONNECT && _buf_used(streambuf) == 0) { | |
201 | eos = true; | |
202 | LOG_DEBUG("end of stream"); | |
203 | memset(m->readbuf + m->readbuf_len, 0, MAD_BUFFER_GUARD); | |
204 | m->readbuf_len += MAD_BUFFER_GUARD; | |
205 | } | |
206 | ||
207 | UNLOCK_S; | |
208 | ||
209 | MAD(m, stream_buffer, &m->stream, m->readbuf, m->readbuf_len); | |
210 | ||
211 | while (true) { | |
212 | size_t frames; | |
213 | s32_t *iptrl; | |
214 | s32_t *iptrr; | |
215 | unsigned max_frames; | |
216 | ||
217 | if (MAD(m, frame_decode, &m->frame, &m->stream) == -1) { | |
218 | decode_state ret; | |
219 | if (!eos && m->stream.error == MAD_ERROR_BUFLEN) { | |
220 | ret = DECODE_RUNNING; | |
221 | } else if (eos && (m->stream.error == MAD_ERROR_BUFLEN || m->stream.error == MAD_ERROR_LOSTSYNC)) { | |
222 | ret = DECODE_COMPLETE; | |
223 | } else if (!MAD_RECOVERABLE(m->stream.error)) { | |
224 | LOG_INFO("mad_frame_decode error: %s - stopping decoder", MAD(m, stream_errorstr, &m->stream)); | |
225 | ret = DECODE_COMPLETE; | |
226 | } else { | |
227 | if (m->stream.error != m->last_error) { | |
228 | // suppress repeat error messages | |
229 | LOG_DEBUG("mad_frame_decode error: %s", MAD(m, stream_errorstr, &m->stream)); | |
230 | } | |
231 | ret = DECODE_RUNNING; | |
232 | } | |
233 | m->last_error = m->stream.error; | |
234 | return ret; | |
235 | }; | |
236 | ||
237 | MAD(m, synth_frame, &m->synth, &m->frame); | |
238 | ||
239 | if (decode.new_stream) { | |
240 | LOCK_O; | |
241 | LOG_INFO("setting track_start"); | |
242 | output.next_sample_rate = decode_newstream(m->synth.pcm.samplerate, output.supported_rates); | |
243 | IF_DSD( output.next_dop = false; ) | |
244 | output.track_start = outputbuf->writep; | |
245 | if (output.fade_mode) _checkfade(true); | |
246 | decode.new_stream = false; | |
247 | UNLOCK_O; | |
248 | } | |
249 | ||
250 | LOCK_O_direct; | |
251 | ||
252 | IF_DIRECT( | |
253 | max_frames = _buf_space(outputbuf) / BYTES_PER_FRAME; | |
254 | ); | |
255 | IF_PROCESS( | |
256 | max_frames = process.max_in_frames - process.in_frames; | |
257 | ); | |
258 | ||
259 | if (m->synth.pcm.length > max_frames) { | |
260 | LOG_WARN("too many samples - dropping samples"); | |
261 | m->synth.pcm.length = max_frames; | |
262 | } | |
263 | ||
264 | frames = m->synth.pcm.length; | |
265 | iptrl = m->synth.pcm.samples[0]; | |
266 | iptrr = m->synth.pcm.samples[ m->synth.pcm.channels - 1 ]; | |
267 | ||
268 | if (m->skip) { | |
269 | u32_t skip = min(m->skip, frames); | |
270 | LOG_DEBUG("gapless: skipping %u frames at start", skip); | |
271 | frames -= skip; | |
272 | m->skip -= skip; | |
273 | iptrl += skip; | |
274 | iptrr += skip; | |
275 | } | |
276 | ||
277 | if (m->samples) { | |
278 | if (m->samples < frames) { | |
279 | LOG_DEBUG("gapless: trimming %u frames from end", frames - m->samples); | |
280 | frames = (size_t)m->samples; | |
281 | } | |
282 | m->samples -= frames; | |
283 | if (m->samples > 0 && eos && !(m->stream.next_frame[0] == 0xff && (m->stream.next_frame[1] & 0xf0) == 0xf0)) { | |
284 | // this is the last frame to be decoded, but more samples expected so we must have skipped, remove padding | |
285 | // note this only works if the padding is less than one frame of 1152 bytes otherswise some gap will remain | |
286 | LOG_DEBUG("gapless: early end - trimming padding from end"); | |
287 | if (frames >= m->padding) { | |
288 | frames -= m->padding; | |
289 | } else { | |
290 | frames = 0; | |
291 | } | |
292 | m->samples = 0; | |
293 | } | |
294 | } | |
295 | ||
296 | LOG_SDEBUG("write %u frames", frames); | |
297 | ||
298 | while (frames > 0) { | |
299 | size_t f, count; | |
300 | s32_t *optr; | |
301 | ||
302 | IF_DIRECT( | |
303 | f = min(frames, _buf_cont_write(outputbuf) / BYTES_PER_FRAME); | |
304 | optr = (s32_t *)outputbuf->writep; | |
305 | ); | |
306 | IF_PROCESS( | |
307 | f = min(frames, process.max_in_frames - process.in_frames); | |
308 | optr = (s32_t *)((u8_t *)process.inbuf + process.in_frames * BYTES_PER_FRAME); | |
309 | ); | |
310 | ||
311 | count = f; | |
312 | ||
313 | while (count--) { | |
314 | *optr++ = scale(*iptrl++); | |
315 | *optr++ = scale(*iptrr++); | |
316 | } | |
317 | ||
318 | frames -= f; | |
319 | ||
320 | IF_DIRECT( | |
321 | _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); | |
322 | ); | |
323 | IF_PROCESS( | |
324 | process.in_frames += f; | |
325 | ); | |
326 | } | |
327 | ||
328 | UNLOCK_O_direct; | |
329 | } | |
330 | ||
331 | return eos ? DECODE_COMPLETE : DECODE_RUNNING; | |
332 | } | |
333 | ||
334 | static void mad_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
335 | if (!m->readbuf) { | |
336 | m->readbuf = malloc(READBUF_SIZE + MAD_BUFFER_GUARD); | |
337 | } | |
338 | m->checktags = 1; | |
339 | m->consume = 0; | |
340 | m->skip = MAD_DELAY; | |
341 | m->samples = 0; | |
342 | m->readbuf_len = 0; | |
343 | m->last_error = MAD_ERROR_NONE; | |
344 | MAD(m, stream_init, &m->stream); | |
345 | MAD(m, frame_init, &m->frame); | |
346 | MAD(m, synth_init, &m->synth); | |
347 | } | |
348 | ||
349 | static void mad_close(void) { | |
350 | mad_synth_finish(&m->synth); // macro only in current version | |
351 | MAD(m, frame_finish, &m->frame); | |
352 | MAD(m, stream_finish, &m->stream); | |
353 | free(m->readbuf); | |
354 | m->readbuf = NULL; | |
355 | } | |
356 | ||
357 | static bool load_mad() { | |
358 | #if !LINKALL | |
359 | void *handle = dlopen(LIBMAD, RTLD_NOW); | |
360 | char *err; | |
361 | ||
362 | if (!handle) { | |
363 | LOG_INFO("dlerror: %s", dlerror()); | |
364 | return false; | |
365 | } | |
366 | ||
367 | m->mad_stream_init = dlsym(handle, "mad_stream_init"); | |
368 | m->mad_frame_init = dlsym(handle, "mad_frame_init"); | |
369 | m->mad_synth_init = dlsym(handle, "mad_synth_init"); | |
370 | m->mad_frame_finish = dlsym(handle, "mad_frame_finish"); | |
371 | m->mad_stream_finish = dlsym(handle, "mad_stream_finish"); | |
372 | m->mad_stream_buffer = dlsym(handle, "mad_stream_buffer"); | |
373 | m->mad_frame_decode = dlsym(handle, "mad_frame_decode"); | |
374 | m->mad_synth_frame = dlsym(handle, "mad_synth_frame"); | |
375 | m->mad_stream_errorstr = dlsym(handle, "mad_stream_errorstr"); | |
376 | ||
377 | if ((err = dlerror()) != NULL) { | |
378 | LOG_INFO("dlerror: %s", err); | |
379 | return false; | |
380 | } | |
381 | ||
382 | LOG_INFO("loaded "LIBMAD); | |
383 | #endif | |
384 | ||
385 | return true; | |
386 | } | |
387 | ||
388 | struct codec *register_mad(void) { | |
389 | static struct codec ret = { | |
390 | 'm', // id | |
391 | "mp3", // types | |
392 | READBUF_SIZE, // min read | |
393 | 206800, // min space | |
394 | mad_open, // open | |
395 | mad_close, // close | |
396 | mad_decode, // decode | |
397 | }; | |
398 | ||
399 | m = malloc(sizeof(struct mad)); | |
400 | if (!m) { | |
401 | return NULL; | |
402 | } | |
403 | ||
404 | m->readbuf = NULL; | |
405 | m->readbuf_len = 0; | |
406 | ||
407 | if (!load_mad()) { | |
408 | return NULL; | |
409 | } | |
410 | ||
411 | LOG_INFO("using mad to decode mp3"); | |
412 | return &ret; | |
413 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 "squeezelite.h" | |
21 | ||
22 | #include <signal.h> | |
23 | ||
24 | #define TITLE "Squeezelite " VERSION ", Copyright 2012-2015 Adrian Smith." | |
25 | ||
26 | #define CODECS_BASE "flac,pcm,mp3,ogg,aac" | |
27 | #if FFMPEG | |
28 | #define CODECS_FF ",wma,alac" | |
29 | #else | |
30 | #define CODECS_FF "" | |
31 | #endif | |
32 | #if DSD | |
33 | #define CODECS_DSD ",dsd" | |
34 | #else | |
35 | #define CODECS_DSD "" | |
36 | #endif | |
37 | #define CODECS_MP3 " (mad,mpg for specific mp3 codec)" | |
38 | ||
39 | #define CODECS CODECS_BASE CODECS_FF CODECS_DSD CODECS_MP3 | |
40 | ||
41 | static void usage(const char *argv0) { | |
42 | printf(TITLE " See -t for license terms\n" | |
43 | "Usage: %s [options]\n" | |
44 | " -s <server>[:<port>]\tConnect to specified server, otherwise uses autodiscovery to find server\n" | |
45 | " -o <output device>\tSpecify output device, default \"default\", - = output to stdout\n" | |
46 | " -l \t\t\tList output devices\n" | |
47 | #if ALSA | |
48 | " -a <b>:<p>:<f>:<m>\tSpecify ALSA params to open output device, b = buffer time in ms or size in bytes, p = period count or size in bytes, f sample format (16|24|24_3|32), m = use mmap (0|1)\n" | |
49 | #endif | |
50 | #if PORTAUDIO | |
51 | #if OSX | |
52 | " -a <l>:<r>\t\tSpecify Portaudio params to open output device, l = target latency in ms, r = allow OSX to resample (0|1)\n" | |
53 | #else | |
54 | " -a <l>\t\tSpecify Portaudio params to open output device, l = target latency in ms\n" | |
55 | #endif | |
56 | #endif | |
57 | " -a <f>\t\tSpecify sample format (16|24|32) of output file when using -o - to output samples to stdout (interleaved little endian only)\n" | |
58 | " -b <stream>:<output>\tSpecify internal Stream and Output buffer sizes in Kbytes\n" | |
59 | " -c <codec1>,<codec2>\tRestrict codecs to those specified, otherwise load all available codecs; known codecs: " CODECS "\n" | |
60 | " -C <timeout>\t\tClose output device when idle after timeout seconds, default is to keep it open while player is 'on'\n" | |
61 | #if !IR | |
62 | " -d <log>=<level>\tSet logging level, logs: all|slimproto|stream|decode|output, level: info|debug|sdebug\n" | |
63 | #else | |
64 | " -d <log>=<level>\tSet logging level, logs: all|slimproto|stream|decode|output|ir, level: info|debug|sdebug\n" | |
65 | #endif | |
66 | " -e <codec1>,<codec2>\tExplicitly exclude native support of one or more codecs; known codecs: " CODECS "\n" | |
67 | " -f <logfile>\t\tWrite debug to logfile\n" | |
68 | #if IR | |
69 | " -i [<filename>]\tEnable lirc remote control support (lirc config file ~/.lircrc used if filename not specified)\n" | |
70 | #endif | |
71 | " -m <mac addr>\t\tSet mac address, format: ab:cd:ef:12:34:56\n" | |
72 | " -M <modelname>\tSet the squeezelite player model name sent to the server (default: " MODEL_NAME_STRING ")\n" | |
73 | " -n <name>\t\tSet the player name\n" | |
74 | " -N <filename>\t\tStore player name in filename to allow server defined name changes to be shared between servers (not supported with -n)\n" | |
75 | #if ALSA | |
76 | " -p <priority>\t\tSet real time priority of output thread (1-99)\n" | |
77 | #endif | |
78 | #if LINUX || FREEBSD | |
79 | " -P <filename>\t\tStore the process id (PID) in filename\n" | |
80 | #endif | |
81 | " -r <rates>[:<delay>]\tSample rates supported, allows output to be off when squeezelite is started; rates = <maxrate>|<minrate>-<maxrate>|<rate1>,<rate2>,<rate3>; delay = optional delay switching rates in ms\n" | |
82 | #if RESAMPLE | |
83 | " -R -u [params]\tResample, params = <recipe>:<flags>:<attenuation>:<precision>:<passband_end>:<stopband_start>:<phase_response>,\n" | |
84 | " \t\t\t recipe = (v|h|m|l|q)(L|I|M)(s) [E|X], E = exception - resample only if native rate not supported, X = async - resample to max rate for device, otherwise to max sync rate\n" | |
85 | " \t\t\t flags = num in hex,\n" | |
86 | " \t\t\t attenuation = attenuation in dB to apply (default is -1db if not explicitly set),\n" | |
87 | " \t\t\t precision = number of bits precision (NB. HQ = 20. VHQ = 28),\n" | |
88 | " \t\t\t passband_end = number in percent (0dB pt. bandwidth to preserve. nyquist = 100%%),\n" | |
89 | " \t\t\t stopband_start = number in percent (Aliasing/imaging control. > passband_end),\n" | |
90 | " \t\t\t phase_response = 0-100 (0 = minimum / 50 = linear / 100 = maximum)\n" | |
91 | #endif | |
92 | #if DSD | |
93 | " -D [delay]\t\tOutput device supports DSD over PCM (DoP), delay = optional delay switching between PCM and DoP in ms\n" | |
94 | #endif | |
95 | #if VISEXPORT | |
96 | " -v \t\t\tVisualiser support\n" | |
97 | #endif | |
98 | # if ALSA | |
99 | " -L \t\t\tList volume controls for output device\n" | |
100 | " -U <control>\t\tUnmute ALSA control and set to full volume (not supported with -V)\n" | |
101 | " -V <control>\t\tUse ALSA control for volume adjustment, otherwise use software volume adjustment\n" | |
102 | #endif | |
103 | #if LINUX || FREEBSD | |
104 | " -z \t\t\tDaemonize\n" | |
105 | #endif | |
106 | " -t \t\t\tLicense terms\n" | |
107 | " -? \t\t\tDisplay this help text\n" | |
108 | "\n" | |
109 | "Build options:" | |
110 | #if LINUX | |
111 | " LINUX" | |
112 | #endif | |
113 | #if WIN | |
114 | " WIN" | |
115 | #endif | |
116 | #if OSX | |
117 | " OSX" | |
118 | #endif | |
119 | #if FREEBSD | |
120 | " FREEBSD" | |
121 | #endif | |
122 | #if ALSA | |
123 | " ALSA" | |
124 | #endif | |
125 | #if PORTAUDIO | |
126 | " PORTAUDIO" | |
127 | #endif | |
128 | #if EVENTFD | |
129 | " EVENTFD" | |
130 | #endif | |
131 | #if SELFPIPE | |
132 | " SELFPIPE" | |
133 | #endif | |
134 | #if WINEVENT | |
135 | " WINEVENT" | |
136 | #endif | |
137 | #if RESAMPLE_MP | |
138 | " RESAMPLE_MP" | |
139 | #else | |
140 | #if RESAMPLE | |
141 | " RESAMPLE" | |
142 | #endif | |
143 | #endif | |
144 | #if FFMPEG | |
145 | " FFMPEG" | |
146 | #endif | |
147 | #if VISEXPORT | |
148 | " VISEXPORT" | |
149 | #endif | |
150 | #if IR | |
151 | " IR" | |
152 | #endif | |
153 | #if DSD | |
154 | " DSD" | |
155 | #endif | |
156 | #if LINKALL | |
157 | " LINKALL" | |
158 | #endif | |
159 | "\n\n", | |
160 | argv0); | |
161 | } | |
162 | ||
163 | static void license(void) { | |
164 | printf(TITLE "\n\n" | |
165 | "This program is free software: you can redistribute it and/or modify\n" | |
166 | "it under the terms of the GNU General Public License as published by\n" | |
167 | "the Free Software Foundation, either version 3 of the License, or\n" | |
168 | "(at your option) any later version.\n\n" | |
169 | "This program is distributed in the hope that it will be useful,\n" | |
170 | "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" | |
171 | "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" | |
172 | "GNU General Public License for more details.\n\n" | |
173 | "You should have received a copy of the GNU General Public License\n" | |
174 | "along with this program. If not, see <http://www.gnu.org/licenses/>.\n\n" | |
175 | #if DSD | |
176 | "Contains dsd2pcm library Copyright 2009, 2011 Sebastian Gesemann which\n" | |
177 | "is subject to its own license.\n\n" | |
178 | #endif | |
179 | ); | |
180 | } | |
181 | ||
182 | static void sighandler(int signum) { | |
183 | slimproto_stop(); | |
184 | ||
185 | // remove ourselves in case above does not work, second SIGINT will cause non gracefull shutdown | |
186 | signal(signum, SIG_DFL); | |
187 | } | |
188 | ||
189 | int main(int argc, char **argv) { | |
190 | char *server = NULL; | |
191 | char *output_device = "default"; | |
192 | char *include_codecs = NULL; | |
193 | char *exclude_codecs = ""; | |
194 | char *name = NULL; | |
195 | char *namefile = NULL; | |
196 | char *modelname = NULL; | |
197 | char *logfile = NULL; | |
198 | u8_t mac[6]; | |
199 | unsigned stream_buf_size = STREAMBUF_SIZE; | |
200 | unsigned output_buf_size = 0; // set later | |
201 | unsigned rates[MAX_SUPPORTED_SAMPLERATES] = { 0 }; | |
202 | unsigned rate_delay = 0; | |
203 | char *resample = NULL; | |
204 | char *output_params = NULL; | |
205 | unsigned idle = 0; | |
206 | #if LINUX || FREEBSD | |
207 | bool daemonize = false; | |
208 | char *pidfile = NULL; | |
209 | FILE *pidfp = NULL; | |
210 | #endif | |
211 | #if ALSA | |
212 | unsigned rt_priority = OUTPUT_RT_PRIORITY; | |
213 | char *output_mixer = NULL; | |
214 | bool output_mixer_unmute = false; | |
215 | #endif | |
216 | #if DSD | |
217 | bool dop = false; | |
218 | unsigned dop_delay = 0; | |
219 | #endif | |
220 | #if VISEXPORT | |
221 | bool visexport = false; | |
222 | #endif | |
223 | #if IR | |
224 | char *lircrc = NULL; | |
225 | #endif | |
226 | ||
227 | log_level log_output = lWARN; | |
228 | log_level log_stream = lWARN; | |
229 | log_level log_decode = lWARN; | |
230 | log_level log_slimproto = lWARN; | |
231 | #if IR | |
232 | log_level log_ir = lWARN; | |
233 | #endif | |
234 | ||
235 | char *optarg = NULL; | |
236 | int optind = 1; | |
237 | int i; | |
238 | ||
239 | #define MAXCMDLINE 512 | |
240 | char cmdline[MAXCMDLINE] = ""; | |
241 | ||
242 | get_mac(mac); | |
243 | ||
244 | for (i = 0; i < argc && (strlen(argv[i]) + strlen(cmdline) + 2 < MAXCMDLINE); i++) { | |
245 | strcat(cmdline, argv[i]); | |
246 | strcat(cmdline, " "); | |
247 | } | |
248 | ||
249 | while (optind < argc && strlen(argv[optind]) >= 2 && argv[optind][0] == '-') { | |
250 | char *opt = argv[optind] + 1; | |
251 | if (strstr("oabcCdefmMnNpPrs" | |
252 | #if ALSA | |
253 | "UV" | |
254 | #endif | |
255 | , opt) && optind < argc - 1) { | |
256 | optarg = argv[optind + 1]; | |
257 | optind += 2; | |
258 | } else if (strstr("ltz?" | |
259 | #if ALSA | |
260 | "L" | |
261 | #endif | |
262 | #if RESAMPLE | |
263 | "uR" | |
264 | #endif | |
265 | #if DSD | |
266 | "D" | |
267 | #endif | |
268 | #if VISEXPORT | |
269 | "v" | |
270 | #endif | |
271 | #if IR | |
272 | "i" | |
273 | #endif | |
274 | ||
275 | , opt)) { | |
276 | optarg = NULL; | |
277 | optind += 1; | |
278 | } else { | |
279 | fprintf(stderr, "\nOption error: -%s\n\n", opt); | |
280 | usage(argv[0]); | |
281 | exit(1); | |
282 | } | |
283 | ||
284 | switch (opt[0]) { | |
285 | case 'o': | |
286 | output_device = optarg; | |
287 | break; | |
288 | case 'a': | |
289 | output_params = optarg; | |
290 | break; | |
291 | case 'b': | |
292 | { | |
293 | char *s = next_param(optarg, ':'); | |
294 | char *o = next_param(NULL, ':'); | |
295 | if (s) stream_buf_size = atoi(s) * 1024; | |
296 | if (o) output_buf_size = atoi(o) * 1024; | |
297 | } | |
298 | break; | |
299 | case 'c': | |
300 | include_codecs = optarg; | |
301 | break; | |
302 | case 'C': | |
303 | if (atoi(optarg) > 0) { | |
304 | idle = atoi(optarg) * 1000; | |
305 | } | |
306 | break; | |
307 | case 'e': | |
308 | exclude_codecs = optarg; | |
309 | break; | |
310 | case 'd': | |
311 | { | |
312 | char *l = strtok(optarg, "="); | |
313 | char *v = strtok(NULL, "="); | |
314 | log_level new = lWARN; | |
315 | if (l && v) { | |
316 | if (!strcmp(v, "info")) new = lINFO; | |
317 | if (!strcmp(v, "debug")) new = lDEBUG; | |
318 | if (!strcmp(v, "sdebug")) new = lSDEBUG; | |
319 | if (!strcmp(l, "all") || !strcmp(l, "slimproto")) log_slimproto = new; | |
320 | if (!strcmp(l, "all") || !strcmp(l, "stream")) log_stream = new; | |
321 | if (!strcmp(l, "all") || !strcmp(l, "decode")) log_decode = new; | |
322 | if (!strcmp(l, "all") || !strcmp(l, "output")) log_output = new; | |
323 | #if IR | |
324 | if (!strcmp(l, "all") || !strcmp(l, "ir")) log_ir = new; | |
325 | #endif | |
326 | } else { | |
327 | fprintf(stderr, "\nDebug settings error: -d %s\n\n", optarg); | |
328 | usage(argv[0]); | |
329 | exit(1); | |
330 | } | |
331 | } | |
332 | break; | |
333 | case 'f': | |
334 | logfile = optarg; | |
335 | break; | |
336 | case 'm': | |
337 | { | |
338 | int byte = 0; | |
339 | char *tmp; | |
340 | if (!strncmp(optarg, "00:04:20", 8)) { | |
341 | LOG_ERROR("ignoring mac address from hardware player range 00:04:20:**:**:**"); | |
342 | } else { | |
343 | char *t = strtok(optarg, ":"); | |
344 | while (t && byte < 6) { | |
345 | mac[byte++] = (u8_t)strtoul(t, &tmp, 16); | |
346 | t = strtok(NULL, ":"); | |
347 | } | |
348 | } | |
349 | } | |
350 | break; | |
351 | case 'M': | |
352 | modelname = optarg; | |
353 | break; | |
354 | case 'r': | |
355 | { | |
356 | char *rstr = next_param(optarg, ':'); | |
357 | char *dstr = next_param(NULL, ':'); | |
358 | if (rstr && strstr(rstr, ",")) { | |
359 | // parse sample rates and sort them | |
360 | char *r = next_param(rstr, ','); | |
361 | unsigned tmp[MAX_SUPPORTED_SAMPLERATES] = { 0 }; | |
362 | int i, j; | |
363 | int last = 999999; | |
364 | for (i = 0; r && i < MAX_SUPPORTED_SAMPLERATES; ++i) { | |
365 | tmp[i] = atoi(r); | |
366 | r = next_param(NULL, ','); | |
367 | } | |
368 | for (i = 0; i < MAX_SUPPORTED_SAMPLERATES; ++i) { | |
369 | int largest = 0; | |
370 | for (j = 0; j < MAX_SUPPORTED_SAMPLERATES; ++j) { | |
371 | if (tmp[j] > largest && tmp[j] < last) { | |
372 | largest = tmp[j]; | |
373 | } | |
374 | } | |
375 | rates[i] = last = largest; | |
376 | } | |
377 | } else if (rstr) { | |
378 | // optstr is <min>-<max> or <max>, extract rates from test rates within this range | |
379 | unsigned ref[] TEST_RATES; | |
380 | char *str1 = next_param(rstr, '-'); | |
381 | char *str2 = next_param(NULL, '-'); | |
382 | unsigned max = str2 ? atoi(str2) : (str1 ? atoi(str1) : ref[0]); | |
383 | unsigned min = str1 && str2 ? atoi(str1) : 0; | |
384 | unsigned tmp; | |
385 | int i, j; | |
386 | if (max < min) { tmp = max; max = min; min = tmp; } | |
387 | rates[0] = max; | |
388 | for (i = 0, j = 1; i < MAX_SUPPORTED_SAMPLERATES; ++i) { | |
389 | if (ref[i] < rates[j-1] && ref[i] >= min) { | |
390 | rates[j++] = ref[i]; | |
391 | } | |
392 | } | |
393 | } | |
394 | if (dstr) { | |
395 | rate_delay = atoi(dstr); | |
396 | } | |
397 | } | |
398 | break; | |
399 | case 's': | |
400 | server = optarg; | |
401 | break; | |
402 | case 'n': | |
403 | name = optarg; | |
404 | break; | |
405 | case 'N': | |
406 | namefile = optarg; | |
407 | break; | |
408 | #if ALSA | |
409 | case 'p': | |
410 | rt_priority = atoi(optarg); | |
411 | if (rt_priority > 99 || rt_priority < 1) { | |
412 | fprintf(stderr, "\nError: invalid priority: %s\n\n", optarg); | |
413 | usage(argv[0]); | |
414 | exit(1); | |
415 | } | |
416 | break; | |
417 | #endif | |
418 | #if LINUX || FREEBSD | |
419 | case 'P': | |
420 | pidfile = optarg; | |
421 | break; | |
422 | #endif | |
423 | case 'l': | |
424 | list_devices(); | |
425 | exit(0); | |
426 | break; | |
427 | #if ALSA | |
428 | case 'L': | |
429 | list_mixers(output_device); | |
430 | exit(0); | |
431 | break; | |
432 | #endif | |
433 | #if RESAMPLE | |
434 | case 'u': | |
435 | case 'R': | |
436 | if (optind < argc && argv[optind] && argv[optind][0] != '-') { | |
437 | resample = argv[optind++]; | |
438 | } else { | |
439 | resample = ""; | |
440 | } | |
441 | break; | |
442 | #endif | |
443 | #if DSD | |
444 | case 'D': | |
445 | dop = true; | |
446 | if (optind < argc && argv[optind] && argv[optind][0] != '-') { | |
447 | dop_delay = atoi(argv[optind++]); | |
448 | } | |
449 | break; | |
450 | #endif | |
451 | #if VISEXPORT | |
452 | case 'v': | |
453 | visexport = true; | |
454 | break; | |
455 | #endif | |
456 | #if ALSA | |
457 | case 'U': | |
458 | output_mixer_unmute = true; | |
459 | case 'V': | |
460 | if (output_mixer) { | |
461 | fprintf(stderr, "-U and -V option should not be used at same time\n"); | |
462 | exit(1); | |
463 | } | |
464 | output_mixer = optarg; | |
465 | break; | |
466 | #endif | |
467 | #if IR | |
468 | case 'i': | |
469 | if (optind < argc && argv[optind] && argv[optind][0] != '-') { | |
470 | lircrc = argv[optind++]; | |
471 | } else { | |
472 | lircrc = "~/.lircrc"; // liblirc_client will expand ~/ | |
473 | } | |
474 | break; | |
475 | #endif | |
476 | #if LINUX || FREEBSD | |
477 | case 'z': | |
478 | daemonize = true; | |
479 | break; | |
480 | #endif | |
481 | case 't': | |
482 | license(); | |
483 | exit(0); | |
484 | case '?': | |
485 | usage(argv[0]); | |
486 | exit(0); | |
487 | default: | |
488 | fprintf(stderr, "Arg error: %s\n", argv[optind]); | |
489 | break; | |
490 | } | |
491 | } | |
492 | ||
493 | // warn if command line includes something which isn't parsed | |
494 | if (optind < argc) { | |
495 | fprintf(stderr, "\nError: command line argument error\n\n"); | |
496 | usage(argv[0]); | |
497 | exit(1); | |
498 | } | |
499 | ||
500 | signal(SIGINT, sighandler); | |
501 | signal(SIGTERM, sighandler); | |
502 | #if defined(SIGQUIT) | |
503 | signal(SIGQUIT, sighandler); | |
504 | #endif | |
505 | #if defined(SIGHUP) | |
506 | signal(SIGHUP, sighandler); | |
507 | #endif | |
508 | ||
509 | // set the output buffer size if not specified on the command line, take account of resampling | |
510 | if (!output_buf_size) { | |
511 | output_buf_size = OUTPUTBUF_SIZE; | |
512 | if (resample) { | |
513 | unsigned scale = 8; | |
514 | if (rates[0]) { | |
515 | scale = rates[0] / 44100; | |
516 | if (scale > 8) scale = 8; | |
517 | if (scale < 1) scale = 1; | |
518 | } | |
519 | output_buf_size *= scale; | |
520 | } | |
521 | } | |
522 | ||
523 | if (logfile) { | |
524 | if (!freopen(logfile, "a", stderr)) { | |
525 | fprintf(stderr, "error opening logfile %s: %s\n", logfile, strerror(errno)); | |
526 | } else { | |
527 | if (log_output >= lINFO || log_stream >= lINFO || log_decode >= lINFO || log_slimproto >= lINFO) { | |
528 | fprintf(stderr, "\n%s\n", cmdline); | |
529 | } | |
530 | } | |
531 | } | |
532 | ||
533 | #if LINUX || FREEBSD | |
534 | if (pidfile) { | |
535 | if (!(pidfp = fopen(pidfile, "w")) ) { | |
536 | fprintf(stderr, "Error opening pidfile %s: %s\n", pidfile, strerror(errno)); | |
537 | exit(1); | |
538 | } | |
539 | pidfile = realpath(pidfile, NULL); // daemonize will change cwd | |
540 | } | |
541 | ||
542 | if (daemonize) { | |
543 | if (daemon(0, logfile ? 1 : 0)) { | |
544 | fprintf(stderr, "error daemonizing: %s\n", strerror(errno)); | |
545 | } | |
546 | } | |
547 | ||
548 | if (pidfp) { | |
549 | fprintf(pidfp, "%d\n", getpid()); | |
550 | fclose(pidfp); | |
551 | } | |
552 | #endif | |
553 | ||
554 | #if WIN | |
555 | winsock_init(); | |
556 | #endif | |
557 | ||
558 | stream_init(log_stream, stream_buf_size); | |
559 | ||
560 | if (!strcmp(output_device, "-")) { | |
561 | output_init_stdout(log_output, output_buf_size, output_params, rates, rate_delay); | |
562 | } else { | |
563 | #if ALSA | |
564 | output_init_alsa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, rt_priority, idle, output_mixer, | |
565 | output_mixer_unmute); | |
566 | #endif | |
567 | #if PORTAUDIO | |
568 | output_init_pa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle); | |
569 | #endif | |
570 | } | |
571 | ||
572 | #if DSD | |
573 | dop_init(dop, dop_delay); | |
574 | #endif | |
575 | ||
576 | #if VISEXPORT | |
577 | if (visexport) { | |
578 | output_vis_init(log_output, mac); | |
579 | } | |
580 | #endif | |
581 | ||
582 | decode_init(log_decode, include_codecs, exclude_codecs); | |
583 | ||
584 | #if RESAMPLE | |
585 | if (resample) { | |
586 | process_init(resample); | |
587 | } | |
588 | #endif | |
589 | ||
590 | #if IR | |
591 | if (lircrc) { | |
592 | ir_init(log_ir, lircrc); | |
593 | } | |
594 | #endif | |
595 | ||
596 | if (name && namefile) { | |
597 | fprintf(stderr, "-n and -N option should not be used at same time\n"); | |
598 | exit(1); | |
599 | } | |
600 | ||
601 | slimproto(log_slimproto, server, mac, name, namefile, modelname); | |
602 | ||
603 | decode_close(); | |
604 | stream_close(); | |
605 | ||
606 | if (!strcmp(output_device, "-")) { | |
607 | output_close_stdout(); | |
608 | } else { | |
609 | #if ALSA | |
610 | output_close_alsa(); | |
611 | #endif | |
612 | #if PORTAUDIO | |
613 | output_close_pa(); | |
614 | #endif | |
615 | } | |
616 | ||
617 | #if IR | |
618 | ir_close(); | |
619 | #endif | |
620 | ||
621 | #if WIN | |
622 | winsock_close(); | |
623 | #endif | |
624 | ||
625 | #if LINUX || FREEBSD | |
626 | if (pidfile) { | |
627 | unlink(pidfile); | |
628 | free(pidfile); | |
629 | } | |
630 | #endif | |
631 | ||
632 | exit(0); | |
633 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 "squeezelite.h" | |
21 | ||
22 | #include <mpg123.h> | |
23 | ||
24 | #define READ_SIZE 512 | |
25 | #define WRITE_SIZE 32 * 1024 | |
26 | ||
27 | struct mpg { | |
28 | mpg123_handle *h; | |
29 | bool use16bit; | |
30 | #if !LINKALL | |
31 | // mpg symbols to be dynamically loaded | |
32 | int (* mpg123_init)(void); | |
33 | int (* mpg123_feature)(const enum mpg123_feature_set); | |
34 | void (* mpg123_rates)(const long **, size_t *); | |
35 | int (* mpg123_format_none)(mpg123_handle *); | |
36 | int (* mpg123_format)(mpg123_handle *, long, int, int); | |
37 | mpg123_handle *(* mpg123_new)(const char*, int *); | |
38 | void (* mpg123_delete)(mpg123_handle *); | |
39 | int (* mpg123_open_feed)(mpg123_handle *); | |
40 | int (* mpg123_decode)(mpg123_handle *, const unsigned char *, size_t, unsigned char *, size_t, size_t *); | |
41 | int (* mpg123_getformat)(mpg123_handle *, long *, int *, int *); | |
42 | const char* (* mpg123_plain_strerror)(int); | |
43 | #endif | |
44 | }; | |
45 | ||
46 | static struct mpg *m; | |
47 | ||
48 | extern log_level loglevel; | |
49 | ||
50 | extern struct buffer *streambuf; | |
51 | extern struct buffer *outputbuf; | |
52 | extern struct streamstate stream; | |
53 | extern struct outputstate output; | |
54 | extern struct decodestate decode; | |
55 | extern struct processstate process; | |
56 | ||
57 | #define LOCK_S mutex_lock(streambuf->mutex) | |
58 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
59 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
60 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
61 | #if PROCESS | |
62 | #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex) | |
63 | #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex) | |
64 | #define LOCK_O_not_direct if (!decode.direct) mutex_lock(outputbuf->mutex) | |
65 | #define UNLOCK_O_not_direct if (!decode.direct) mutex_unlock(outputbuf->mutex) | |
66 | #define IF_DIRECT(x) if (decode.direct) { x } | |
67 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
68 | #else | |
69 | #define LOCK_O_direct mutex_lock(outputbuf->mutex) | |
70 | #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex) | |
71 | #define LOCK_O_not_direct | |
72 | #define UNLOCK_O_not_direct | |
73 | #define IF_DIRECT(x) { x } | |
74 | #define IF_PROCESS(x) | |
75 | #endif | |
76 | ||
77 | #if LINKALL | |
78 | #define MPG123(h, fn, ...) (mpg123_ ## fn)(__VA_ARGS__) | |
79 | #else | |
80 | #define MPG123(h, fn, ...) (h)->mpg123_##fn(__VA_ARGS__) | |
81 | #endif | |
82 | ||
83 | static decode_state mpg_decode(void) { | |
84 | size_t bytes, space, size; | |
85 | int ret; | |
86 | u8_t *write_buf; | |
87 | ||
88 | LOCK_S; | |
89 | LOCK_O_direct; | |
90 | bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
91 | ||
92 | IF_DIRECT( | |
93 | space = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)); | |
94 | write_buf = outputbuf->writep; | |
95 | ); | |
96 | IF_PROCESS( | |
97 | space = process.max_in_frames; | |
98 | write_buf = process.inbuf; | |
99 | ); | |
100 | ||
101 | bytes = min(bytes, READ_SIZE); | |
102 | space = min(space, WRITE_SIZE); | |
103 | ||
104 | if (m->use16bit) { | |
105 | space = (space / BYTES_PER_FRAME) * 4; | |
106 | } | |
107 | ||
108 | // only get the new stream information on first call so we can reset decode.direct appropriately | |
109 | if (decode.new_stream) { | |
110 | space = 0; | |
111 | } | |
112 | ||
113 | ret = MPG123(m, decode, m->h, streambuf->readp, bytes, write_buf, space, &size); | |
114 | ||
115 | if (ret == MPG123_NEW_FORMAT) { | |
116 | ||
117 | if (decode.new_stream) { | |
118 | long rate; | |
119 | int channels, enc; | |
120 | ||
121 | MPG123(m, getformat, m->h, &rate, &channels, &enc); | |
122 | ||
123 | LOG_INFO("setting track_start"); | |
124 | LOCK_O_not_direct; | |
125 | output.next_sample_rate = decode_newstream(rate, output.supported_rates); | |
126 | IF_DSD( output.next_dop = false; ) | |
127 | output.track_start = outputbuf->writep; | |
128 | if (output.fade_mode) _checkfade(true); | |
129 | decode.new_stream = false; | |
130 | UNLOCK_O_not_direct; | |
131 | ||
132 | } else { | |
133 | LOG_WARN("format change mid stream - not supported"); | |
134 | } | |
135 | } | |
136 | ||
137 | // expand 16bit output to 32bit samples | |
138 | if (m->use16bit) { | |
139 | s16_t *iptr; | |
140 | s32_t *optr; | |
141 | size_t count = size / 2; | |
142 | size = count * 4; | |
143 | iptr = (s16_t *)write_buf + count; | |
144 | optr = (s32_t *)write_buf + count; | |
145 | while (count--) { | |
146 | *--optr = *--iptr << 16; | |
147 | } | |
148 | } | |
149 | ||
150 | _buf_inc_readp(streambuf, bytes); | |
151 | ||
152 | IF_DIRECT( | |
153 | _buf_inc_writep(outputbuf, size); | |
154 | ); | |
155 | IF_PROCESS( | |
156 | process.in_frames = size / BYTES_PER_FRAME; | |
157 | ); | |
158 | ||
159 | UNLOCK_O_direct; | |
160 | ||
161 | LOG_SDEBUG("write %u frames", size / BYTES_PER_FRAME); | |
162 | ||
163 | if (ret == MPG123_DONE || (bytes == 0 && size == 0 && stream.state <= DISCONNECT)) { | |
164 | UNLOCK_S; | |
165 | LOG_INFO("stream complete"); | |
166 | return DECODE_COMPLETE; | |
167 | } | |
168 | ||
169 | UNLOCK_S; | |
170 | ||
171 | if (ret == MPG123_ERR) { | |
172 | LOG_WARN("Error"); | |
173 | return DECODE_COMPLETE; | |
174 | } | |
175 | ||
176 | // OK and NEED_MORE keep running | |
177 | return DECODE_RUNNING; | |
178 | } | |
179 | ||
180 | static void mpg_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
181 | int err; | |
182 | const long *list; | |
183 | size_t count, i; | |
184 | ||
185 | if (m->h) { | |
186 | MPG123(m, delete, m->h); | |
187 | } | |
188 | ||
189 | m->h = MPG123(m, new, NULL, &err); | |
190 | ||
191 | if (m->h == NULL) { | |
192 | LOG_WARN("new error: %s", MPG123(m, plain_strerror, err)); | |
193 | } | |
194 | ||
195 | // restrict output to 32bit or 16bit signed 2 channel based on library capability | |
196 | MPG123(m, rates, &list, &count); | |
197 | MPG123(m, format_none, m->h); | |
198 | for (i = 0; i < count; i++) { | |
199 | MPG123(m, format, m->h, list[i], 2, m->use16bit ? MPG123_ENC_SIGNED_16 : MPG123_ENC_SIGNED_32); | |
200 | } | |
201 | ||
202 | err = MPG123(m, open_feed, m->h); | |
203 | ||
204 | if (err) { | |
205 | LOG_WARN("open feed error: %s", MPG123(m, plain_strerror, err)); | |
206 | } | |
207 | } | |
208 | ||
209 | static void mpg_close(void) { | |
210 | MPG123(m, delete, m->h); | |
211 | m->h = NULL; | |
212 | } | |
213 | ||
214 | static bool load_mpg() { | |
215 | #if !LINKALL | |
216 | void *handle = dlopen(LIBMPG, RTLD_NOW); | |
217 | char *err; | |
218 | ||
219 | if (!handle) { | |
220 | LOG_INFO("dlerror: %s", dlerror()); | |
221 | return false; | |
222 | } | |
223 | ||
224 | m->mpg123_init = dlsym(handle, "mpg123_init"); | |
225 | m->mpg123_feature = dlsym(handle, "mpg123_feature"); | |
226 | m->mpg123_rates = dlsym(handle, "mpg123_rates"); | |
227 | m->mpg123_format_none = dlsym(handle, "mpg123_format_none"); | |
228 | m->mpg123_format = dlsym(handle, "mpg123_format"); | |
229 | m->mpg123_new = dlsym(handle, "mpg123_new"); | |
230 | m->mpg123_delete = dlsym(handle, "mpg123_delete"); | |
231 | m->mpg123_open_feed = dlsym(handle, "mpg123_open_feed"); | |
232 | m->mpg123_decode = dlsym(handle, "mpg123_decode"); | |
233 | m->mpg123_getformat = dlsym(handle, "mpg123_getformat"); | |
234 | m->mpg123_plain_strerror = dlsym(handle, "mpg123_plain_strerror"); | |
235 | ||
236 | if ((err = dlerror()) != NULL) { | |
237 | LOG_INFO("dlerror: %s", err); | |
238 | return false; | |
239 | } | |
240 | ||
241 | LOG_INFO("loaded "LIBMPG); | |
242 | #endif | |
243 | ||
244 | return true; | |
245 | } | |
246 | ||
247 | struct codec *register_mpg(void) { | |
248 | static struct codec ret = { | |
249 | 'm', // id | |
250 | "mp3", // types | |
251 | READ_SIZE, // min read | |
252 | WRITE_SIZE, // min space | |
253 | mpg_open, // open | |
254 | mpg_close, // close | |
255 | mpg_decode, // decode | |
256 | }; | |
257 | ||
258 | m = malloc(sizeof(struct mpg)); | |
259 | if (!m) { | |
260 | return NULL; | |
261 | } | |
262 | ||
263 | m->h = NULL; | |
264 | ||
265 | if (!load_mpg()) { | |
266 | return NULL; | |
267 | } | |
268 | ||
269 | MPG123(m, init); | |
270 | ||
271 | m->use16bit = MPG123(m, feature, MPG123_FEATURE_OUTPUT_32BIT); | |
272 | ||
273 | LOG_INFO("using mpg to decode mp3"); | |
274 | return &ret; | |
275 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // Common output function | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | static log_level loglevel; | |
25 | ||
26 | struct outputstate output; | |
27 | ||
28 | static struct buffer buf; | |
29 | ||
30 | struct buffer *outputbuf = &buf; | |
31 | ||
32 | u8_t *silencebuf; | |
33 | #if DSD | |
34 | u8_t *silencebuf_dop; | |
35 | #endif | |
36 | ||
37 | #define LOCK mutex_lock(outputbuf->mutex) | |
38 | #define UNLOCK mutex_unlock(outputbuf->mutex) | |
39 | ||
40 | // functions starting _* are called with mutex locked | |
41 | ||
42 | frames_t _output_frames(frames_t avail) { | |
43 | ||
44 | frames_t frames, size; | |
45 | bool silence; | |
46 | ||
47 | s32_t cross_gain_in = 0, cross_gain_out = 0; s32_t *cross_ptr = NULL; | |
48 | ||
49 | s32_t gainL = output.current_replay_gain ? gain(output.gainL, output.current_replay_gain) : output.gainL; | |
50 | s32_t gainR = output.current_replay_gain ? gain(output.gainR, output.current_replay_gain) : output.gainR; | |
51 | ||
52 | if (output.invert) { gainL = -gainL; gainR = -gainR; } | |
53 | ||
54 | frames = _buf_used(outputbuf) / BYTES_PER_FRAME; | |
55 | silence = false; | |
56 | ||
57 | // start when threshold met | |
58 | if (output.state == OUTPUT_BUFFER && frames > output.threshold * output.next_sample_rate / 100 && frames > output.start_frames) { | |
59 | output.state = OUTPUT_RUNNING; | |
60 | LOG_INFO("start buffer frames: %u", frames); | |
61 | wake_controller(); | |
62 | } | |
63 | ||
64 | // skip ahead - consume outputbuf but play nothing | |
65 | if (output.state == OUTPUT_SKIP_FRAMES) { | |
66 | if (frames > 0) { | |
67 | frames_t skip = min(frames, output.skip_frames); | |
68 | LOG_INFO("skip %u of %u frames", skip, output.skip_frames); | |
69 | frames -= skip; | |
70 | output.frames_played += skip; | |
71 | while (skip > 0) { | |
72 | frames_t cont_frames = min(skip, _buf_cont_read(outputbuf) / BYTES_PER_FRAME); | |
73 | skip -= cont_frames; | |
74 | _buf_inc_readp(outputbuf, cont_frames * BYTES_PER_FRAME); | |
75 | } | |
76 | } | |
77 | output.state = OUTPUT_RUNNING; | |
78 | } | |
79 | ||
80 | // pause frames - play silence for required frames | |
81 | if (output.state == OUTPUT_PAUSE_FRAMES) { | |
82 | LOG_INFO("pause %u frames", output.pause_frames); | |
83 | if (output.pause_frames == 0) { | |
84 | output.state = OUTPUT_RUNNING; | |
85 | } else { | |
86 | silence = true; | |
87 | frames = min(avail, output.pause_frames); | |
88 | frames = min(frames, MAX_SILENCE_FRAMES); | |
89 | output.pause_frames -= frames; | |
90 | } | |
91 | } | |
92 | ||
93 | // start at - play silence until jiffies reached | |
94 | if (output.state == OUTPUT_START_AT) { | |
95 | u32_t now = gettime_ms(); | |
96 | if (now >= output.start_at || output.start_at > now + 10000) { | |
97 | output.state = OUTPUT_RUNNING; | |
98 | } else { | |
99 | u32_t delta_frames = (output.start_at - now) * output.current_sample_rate / 1000; | |
100 | silence = true; | |
101 | frames = min(avail, delta_frames); | |
102 | frames = min(frames, MAX_SILENCE_FRAMES); | |
103 | } | |
104 | } | |
105 | ||
106 | // play silence if buffering or no frames | |
107 | if (output.state <= OUTPUT_BUFFER || frames == 0) { | |
108 | silence = true; | |
109 | frames = min(avail, MAX_SILENCE_FRAMES); | |
110 | } | |
111 | ||
112 | LOG_SDEBUG("avail: %d frames: %d silence: %d", avail, frames, silence); | |
113 | frames = min(frames, avail); | |
114 | size = frames; | |
115 | ||
116 | while (size > 0) { | |
117 | frames_t out_frames; | |
118 | frames_t cont_frames = _buf_cont_read(outputbuf) / BYTES_PER_FRAME; | |
119 | int wrote; | |
120 | ||
121 | if (output.track_start && !silence) { | |
122 | if (output.track_start == outputbuf->readp) { | |
123 | unsigned delay = 0; | |
124 | if (output.current_sample_rate != output.next_sample_rate) { | |
125 | delay = output.rate_delay; | |
126 | } | |
127 | IF_DSD( | |
128 | if (output.dop != output.next_dop) { | |
129 | delay = output.dop_delay; | |
130 | } | |
131 | ) | |
132 | frames -= size; | |
133 | // add silence delay in two halves, before and after track start on rate or pcm-dop change | |
134 | if (delay) { | |
135 | output.state = OUTPUT_PAUSE_FRAMES; | |
136 | if (!output.delay_active) { | |
137 | output.pause_frames = output.current_sample_rate * delay / 2000; | |
138 | output.delay_active = true; // first delay - don't process track start | |
139 | break; | |
140 | } else { | |
141 | output.pause_frames = output.next_sample_rate * delay / 2000; | |
142 | output.delay_active = false; // second delay - process track start | |
143 | } | |
144 | } | |
145 | LOG_INFO("track start sample rate: %u replay_gain: %u", output.next_sample_rate, output.next_replay_gain); | |
146 | output.frames_played = 0; | |
147 | output.track_started = true; | |
148 | output.track_start_time = gettime_ms(); | |
149 | output.current_sample_rate = output.next_sample_rate; | |
150 | IF_DSD( | |
151 | output.dop = output.next_dop; | |
152 | ) | |
153 | if (!output.fade == FADE_ACTIVE || !output.fade_mode == FADE_CROSSFADE) { | |
154 | output.current_replay_gain = output.next_replay_gain; | |
155 | } | |
156 | output.track_start = NULL; | |
157 | break; | |
158 | } else if (output.track_start > outputbuf->readp) { | |
159 | // reduce cont_frames so we find the next track start at beginning of next chunk | |
160 | cont_frames = min(cont_frames, (output.track_start - outputbuf->readp) / BYTES_PER_FRAME); | |
161 | } | |
162 | } | |
163 | ||
164 | IF_DSD( | |
165 | if (output.dop) { | |
166 | gainL = gainR = FIXED_ONE; | |
167 | } | |
168 | ) | |
169 | ||
170 | if (output.fade && !silence) { | |
171 | if (output.fade == FADE_DUE) { | |
172 | if (output.fade_start == outputbuf->readp) { | |
173 | LOG_INFO("fade start reached"); | |
174 | output.fade = FADE_ACTIVE; | |
175 | } else if (output.fade_start > outputbuf->readp) { | |
176 | cont_frames = min(cont_frames, (output.fade_start - outputbuf->readp) / BYTES_PER_FRAME); | |
177 | } | |
178 | } | |
179 | if (output.fade == FADE_ACTIVE) { | |
180 | // find position within fade | |
181 | frames_t cur_f = outputbuf->readp >= output.fade_start ? (outputbuf->readp - output.fade_start) / BYTES_PER_FRAME : | |
182 | (outputbuf->readp + outputbuf->size - output.fade_start) / BYTES_PER_FRAME; | |
183 | frames_t dur_f = output.fade_end >= output.fade_start ? (output.fade_end - output.fade_start) / BYTES_PER_FRAME : | |
184 | (output.fade_end + outputbuf->size - output.fade_start) / BYTES_PER_FRAME; | |
185 | if (cur_f >= dur_f) { | |
186 | if (output.fade_mode == FADE_INOUT && output.fade_dir == FADE_DOWN) { | |
187 | LOG_INFO("fade down complete, starting fade up"); | |
188 | output.fade_dir = FADE_UP; | |
189 | output.fade_start = outputbuf->readp; | |
190 | output.fade_end = outputbuf->readp + dur_f * BYTES_PER_FRAME; | |
191 | if (output.fade_end >= outputbuf->wrap) { | |
192 | output.fade_end -= outputbuf->size; | |
193 | } | |
194 | cur_f = 0; | |
195 | } else if (output.fade_mode == FADE_CROSSFADE) { | |
196 | LOG_INFO("crossfade complete"); | |
197 | if (_buf_used(outputbuf) >= dur_f * BYTES_PER_FRAME) { | |
198 | _buf_inc_readp(outputbuf, dur_f * BYTES_PER_FRAME); | |
199 | LOG_INFO("skipped crossfaded start"); | |
200 | } else { | |
201 | LOG_WARN("unable to skip crossfaded start"); | |
202 | } | |
203 | output.fade = FADE_INACTIVE; | |
204 | output.current_replay_gain = output.next_replay_gain; | |
205 | } else { | |
206 | LOG_INFO("fade complete"); | |
207 | output.fade = FADE_INACTIVE; | |
208 | } | |
209 | } | |
210 | // if fade in progress set fade gain, ensure cont_frames reduced so we get to end of fade at start of chunk | |
211 | if (output.fade) { | |
212 | if (output.fade_end > outputbuf->readp) { | |
213 | cont_frames = min(cont_frames, (output.fade_end - outputbuf->readp) / BYTES_PER_FRAME); | |
214 | } | |
215 | if (output.fade_dir == FADE_UP || output.fade_dir == FADE_DOWN) { | |
216 | // fade in, in-out, out handled via altering standard gain | |
217 | s32_t fade_gain; | |
218 | if (output.fade_dir == FADE_DOWN) { | |
219 | cur_f = dur_f - cur_f; | |
220 | } | |
221 | fade_gain = to_gain((float)cur_f / (float)dur_f); | |
222 | gainL = gain(gainL, fade_gain); | |
223 | gainR = gain(gainR, fade_gain); | |
224 | if (output.invert) { gainL = -gainL; gainR = -gainR; } | |
225 | } | |
226 | if (output.fade_dir == FADE_CROSS) { | |
227 | // cross fade requires special treatment - performed later based on these values | |
228 | // support different replay gain for old and new track by retaining old value until crossfade completes | |
229 | if (_buf_used(outputbuf) / BYTES_PER_FRAME > dur_f + size) { | |
230 | cross_gain_in = to_gain((float)cur_f / (float)dur_f); | |
231 | cross_gain_out = FIXED_ONE - cross_gain_in; | |
232 | if (output.current_replay_gain) { | |
233 | cross_gain_out = gain(cross_gain_out, output.current_replay_gain); | |
234 | } | |
235 | if (output.next_replay_gain) { | |
236 | cross_gain_in = gain(cross_gain_in, output.next_replay_gain); | |
237 | } | |
238 | gainL = output.gainL; | |
239 | gainR = output.gainR; | |
240 | if (output.invert) { gainL = -gainL; gainR = -gainR; } | |
241 | cross_ptr = (s32_t *)(output.fade_end + cur_f * BYTES_PER_FRAME); | |
242 | } else { | |
243 | LOG_INFO("unable to continue crossfade - too few samples"); | |
244 | output.fade = FADE_INACTIVE; | |
245 | } | |
246 | } | |
247 | } | |
248 | } | |
249 | } | |
250 | ||
251 | out_frames = !silence ? min(size, cont_frames) : size; | |
252 | ||
253 | wrote = output.write_cb(out_frames, silence, gainL, gainR, cross_gain_in, cross_gain_out, &cross_ptr); | |
254 | ||
255 | if (wrote <= 0) { | |
256 | frames -= size; | |
257 | break; | |
258 | } else { | |
259 | out_frames = (frames_t)wrote; | |
260 | } | |
261 | ||
262 | size -= out_frames; | |
263 | ||
264 | _vis_export(outputbuf, &output, out_frames, silence); | |
265 | ||
266 | if (!silence) { | |
267 | _buf_inc_readp(outputbuf, out_frames * BYTES_PER_FRAME); | |
268 | output.frames_played += out_frames; | |
269 | } | |
270 | } | |
271 | ||
272 | LOG_SDEBUG("wrote %u frames", frames); | |
273 | ||
274 | return frames; | |
275 | } | |
276 | ||
277 | void _checkfade(bool start) { | |
278 | frames_t bytes; | |
279 | ||
280 | LOG_INFO("fade mode: %u duration: %u %s", output.fade_mode, output.fade_secs, start ? "track-start" : "track-end"); | |
281 | ||
282 | bytes = output.next_sample_rate * BYTES_PER_FRAME * output.fade_secs; | |
283 | if (output.fade_mode == FADE_INOUT) { | |
284 | bytes /= 2; | |
285 | } | |
286 | ||
287 | if (start && (output.fade_mode == FADE_IN || (output.fade_mode == FADE_INOUT && _buf_used(outputbuf) == 0))) { | |
288 | bytes = min(bytes, outputbuf->size - BYTES_PER_FRAME); // shorter than full buffer otherwise start and end align | |
289 | LOG_INFO("fade IN: %u frames", bytes / BYTES_PER_FRAME); | |
290 | output.fade = FADE_DUE; | |
291 | output.fade_dir = FADE_UP; | |
292 | output.fade_start = outputbuf->writep; | |
293 | output.fade_end = output.fade_start + bytes; | |
294 | if (output.fade_end >= outputbuf->wrap) { | |
295 | output.fade_end -= outputbuf->size; | |
296 | } | |
297 | } | |
298 | ||
299 | if (!start && (output.fade_mode == FADE_OUT || output.fade_mode == FADE_INOUT)) { | |
300 | bytes = min(_buf_used(outputbuf), bytes); | |
301 | LOG_INFO("fade %s: %u frames", output.fade_mode == FADE_INOUT ? "IN-OUT" : "OUT", bytes / BYTES_PER_FRAME); | |
302 | output.fade = FADE_DUE; | |
303 | output.fade_dir = FADE_DOWN; | |
304 | output.fade_start = outputbuf->writep - bytes; | |
305 | if (output.fade_start < outputbuf->buf) { | |
306 | output.fade_start += outputbuf->size; | |
307 | } | |
308 | output.fade_end = outputbuf->writep; | |
309 | } | |
310 | ||
311 | if (start && output.fade_mode == FADE_CROSSFADE) { | |
312 | if (_buf_used(outputbuf) != 0) { | |
313 | if (output.next_sample_rate != output.current_sample_rate) { | |
314 | LOG_INFO("crossfade disabled as sample rates differ"); | |
315 | return; | |
316 | } | |
317 | bytes = min(bytes, _buf_used(outputbuf)); // max of current remaining samples from previous track | |
318 | bytes = min(bytes, (frames_t)(outputbuf->size * 0.9)); // max of 90% of outputbuf as we consume additional buffer during crossfade | |
319 | LOG_INFO("CROSSFADE: %u frames", bytes / BYTES_PER_FRAME); | |
320 | output.fade = FADE_DUE; | |
321 | output.fade_dir = FADE_CROSS; | |
322 | output.fade_start = outputbuf->writep - bytes; | |
323 | if (output.fade_start < outputbuf->buf) { | |
324 | output.fade_start += outputbuf->size; | |
325 | } | |
326 | output.fade_end = outputbuf->writep; | |
327 | output.track_start = output.fade_start; | |
328 | } else if (outputbuf->size == OUTPUTBUF_SIZE && outputbuf->readp == outputbuf->buf) { | |
329 | // if default setting used and nothing in buffer attempt to resize to provide full crossfade support | |
330 | LOG_INFO("resize outputbuf for crossfade"); | |
331 | _buf_resize(outputbuf, OUTPUTBUF_SIZE_CROSSFADE); | |
332 | #if LINUX || FREEBSD | |
333 | touch_memory(outputbuf->buf, outputbuf->size); | |
334 | #endif | |
335 | } | |
336 | } | |
337 | } | |
338 | ||
339 | void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[], unsigned idle) { | |
340 | unsigned i; | |
341 | ||
342 | loglevel = level; | |
343 | ||
344 | output_buf_size = output_buf_size - (output_buf_size % BYTES_PER_FRAME); | |
345 | LOG_DEBUG("outputbuf size: %u", output_buf_size); | |
346 | ||
347 | buf_init(outputbuf, output_buf_size); | |
348 | if (!outputbuf->buf) { | |
349 | LOG_ERROR("unable to malloc output buffer"); | |
350 | exit(0); | |
351 | } | |
352 | ||
353 | silencebuf = malloc(MAX_SILENCE_FRAMES * BYTES_PER_FRAME); | |
354 | if (!silencebuf) { | |
355 | LOG_ERROR("unable to malloc silence buffer"); | |
356 | exit(0); | |
357 | } | |
358 | memset(silencebuf, 0, MAX_SILENCE_FRAMES * BYTES_PER_FRAME); | |
359 | ||
360 | IF_DSD( | |
361 | silencebuf_dop = malloc(MAX_SILENCE_FRAMES * BYTES_PER_FRAME); | |
362 | if (!silencebuf_dop) { | |
363 | LOG_ERROR("unable to malloc silence dop buffer"); | |
364 | exit(0); | |
365 | } | |
366 | dop_silence_frames((u32_t *)silencebuf_dop, MAX_SILENCE_FRAMES); | |
367 | ) | |
368 | ||
369 | LOG_DEBUG("idle timeout: %u", idle); | |
370 | ||
371 | output.state = idle ? OUTPUT_OFF: OUTPUT_STOPPED; | |
372 | output.device = device; | |
373 | output.fade = FADE_INACTIVE; | |
374 | output.invert = false; | |
375 | output.error_opening = false; | |
376 | output.idle_to = (u32_t) idle; | |
377 | ||
378 | if (!rates[0]) { | |
379 | if (!test_open(output.device, output.supported_rates)) { | |
380 | LOG_ERROR("unable to open output device"); | |
381 | exit(0); | |
382 | } | |
383 | } else { | |
384 | for (i = 0; i < MAX_SUPPORTED_SAMPLERATES; ++i) { | |
385 | output.supported_rates[i] = rates[i]; | |
386 | } | |
387 | } | |
388 | ||
389 | // set initial sample rate, preferring 44100 | |
390 | for (i = 0; i < MAX_SUPPORTED_SAMPLERATES; ++i) { | |
391 | if (output.supported_rates[i] == 44100) { | |
392 | output.default_sample_rate = 44100; | |
393 | break; | |
394 | } | |
395 | } | |
396 | if (!output.default_sample_rate) { | |
397 | output.default_sample_rate = output.supported_rates[0]; | |
398 | } | |
399 | ||
400 | output.current_sample_rate = output.default_sample_rate; | |
401 | ||
402 | if (loglevel >= lINFO) { | |
403 | char rates_buf[10 * MAX_SUPPORTED_SAMPLERATES] = ""; | |
404 | for (i = 0; output.supported_rates[i]; ++i) { | |
405 | char s[10]; | |
406 | sprintf(s, "%d ", output.supported_rates[i]); | |
407 | strcat(rates_buf, s); | |
408 | } | |
409 | LOG_INFO("supported rates: %s", rates_buf); | |
410 | } | |
411 | } | |
412 | ||
413 | void output_close_common(void) { | |
414 | buf_destroy(outputbuf); | |
415 | free(silencebuf); | |
416 | IF_DSD( | |
417 | free(silencebuf_dop); | |
418 | ) | |
419 | } | |
420 | ||
421 | void output_flush(void) { | |
422 | LOG_INFO("flush output buffer"); | |
423 | buf_flush(outputbuf); | |
424 | LOCK; | |
425 | output.fade = FADE_INACTIVE; | |
426 | if (output.state != OUTPUT_OFF) { | |
427 | output.state = OUTPUT_STOPPED; | |
428 | if (output.error_opening) { | |
429 | output.current_sample_rate = output.default_sample_rate; | |
430 | } | |
431 | output.delay_active = false; | |
432 | } | |
433 | output.frames_played = 0; | |
434 | UNLOCK; | |
435 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // Output using Alsa | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #if ALSA | |
25 | ||
26 | #include <alsa/asoundlib.h> | |
27 | #include <sys/mman.h> | |
28 | #include <malloc.h> | |
29 | #include <math.h> | |
30 | ||
31 | #define MAX_DEVICE_LEN 128 | |
32 | ||
33 | static snd_pcm_format_t fmts[] = { SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S16_LE, | |
34 | SND_PCM_FORMAT_UNKNOWN }; | |
35 | ||
36 | #if SL_LITTLE_ENDIAN | |
37 | #define NATIVE_FORMAT SND_PCM_FORMAT_S32_LE | |
38 | #else | |
39 | #define NATIVE_FORMAT SND_PCM_FORMAT_S32_BE | |
40 | #endif | |
41 | ||
42 | // ouput device | |
43 | static struct { | |
44 | char device[MAX_DEVICE_LEN + 1]; | |
45 | snd_pcm_format_t format; | |
46 | snd_pcm_uframes_t buffer_size; | |
47 | snd_pcm_uframes_t period_size; | |
48 | unsigned rate; | |
49 | bool mmap; | |
50 | bool reopen; | |
51 | u8_t *write_buf; | |
52 | const char *volume_mixer_name; | |
53 | int volume_mixer_index; | |
54 | } alsa; | |
55 | ||
56 | static snd_pcm_t *pcmp = NULL; | |
57 | ||
58 | extern u8_t *silencebuf; | |
59 | #if DSD | |
60 | extern u8_t *silencebuf_dop; | |
61 | #endif | |
62 | ||
63 | static log_level loglevel; | |
64 | ||
65 | static bool running = true; | |
66 | ||
67 | extern struct outputstate output; | |
68 | extern struct buffer *outputbuf; | |
69 | ||
70 | #define LOCK mutex_lock(outputbuf->mutex) | |
71 | #define UNLOCK mutex_unlock(outputbuf->mutex) | |
72 | ||
73 | void list_devices(void) { | |
74 | void **hints, **n; | |
75 | if (snd_device_name_hint(-1, "pcm", &hints) >= 0) { | |
76 | n = hints; | |
77 | printf("Output devices:\n"); | |
78 | while (*n) { | |
79 | char *name = snd_device_name_get_hint(*n, "NAME"); | |
80 | char *desc = snd_device_name_get_hint(*n, "DESC"); | |
81 | if (name) printf(" %-30s", name); | |
82 | if (desc) { | |
83 | char *s1 = strtok(desc, "\n"); | |
84 | char *s2 = strtok(NULL, "\n"); | |
85 | if (s1) printf(" - %s", s1); | |
86 | if (s2) printf(" - %s", s2); | |
87 | } | |
88 | printf("\n"); | |
89 | if (name) free(name); | |
90 | if (desc) free(desc); | |
91 | n++; | |
92 | } | |
93 | snd_device_name_free_hint(hints); | |
94 | } | |
95 | printf("\n"); | |
96 | } | |
97 | ||
98 | void list_mixers(const char *output_device) { | |
99 | int err; | |
100 | snd_mixer_t *handle; | |
101 | snd_mixer_selem_id_t *sid; | |
102 | snd_mixer_elem_t *elem; | |
103 | snd_mixer_selem_id_alloca(&sid); | |
104 | ||
105 | LOG_INFO("listing mixers for: %s", output_device); | |
106 | ||
107 | if ((err = snd_mixer_open(&handle, 0)) < 0) { | |
108 | LOG_ERROR("open error: %s", snd_strerror(err)); | |
109 | return; | |
110 | } | |
111 | if ((err = snd_mixer_attach(handle, output_device)) < 0) { | |
112 | LOG_ERROR("attach error: %s", snd_strerror(err)); | |
113 | snd_mixer_close(handle); | |
114 | return; | |
115 | } | |
116 | if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { | |
117 | LOG_ERROR("register error: %s", snd_strerror(err)); | |
118 | snd_mixer_close(handle); | |
119 | return; | |
120 | } | |
121 | if ((err = snd_mixer_load(handle)) < 0) { | |
122 | LOG_ERROR("load error: %s", snd_strerror(err)); | |
123 | snd_mixer_close(handle); | |
124 | return; | |
125 | } | |
126 | ||
127 | printf("Volume controls for %s\n", output_device); | |
128 | for (elem = snd_mixer_first_elem(handle); elem; elem = snd_mixer_elem_next(elem)) { | |
129 | if (snd_mixer_selem_has_playback_volume(elem)) { | |
130 | snd_mixer_selem_get_id(elem, sid); | |
131 | printf(" %s", snd_mixer_selem_id_get_name(sid)); | |
132 | if (snd_mixer_selem_id_get_index(sid)) { | |
133 | printf(",%d", snd_mixer_selem_id_get_index(sid)); | |
134 | } | |
135 | printf("\n"); | |
136 | } | |
137 | } | |
138 | printf("\n"); | |
139 | ||
140 | snd_mixer_close(handle); | |
141 | } | |
142 | ||
143 | #define MINVOL_DB 72 // LMS volume map for SqueezePlay sends values in range ~ -72..0 dB | |
144 | ||
145 | static void set_mixer(const char *device, const char *mixer, int mixer_index, bool setmax, float ldB, float rdB) { | |
146 | int err; | |
147 | long nleft, nright; | |
148 | long min, max; | |
149 | snd_mixer_t *handle; | |
150 | snd_mixer_selem_id_t *sid; | |
151 | snd_mixer_elem_t* elem; | |
152 | ||
153 | if ((err = snd_mixer_open(&handle, 0)) < 0) { | |
154 | LOG_ERROR("open error: %s", snd_strerror(err)); | |
155 | return; | |
156 | } | |
157 | if ((err = snd_mixer_attach(handle, device)) < 0) { | |
158 | LOG_ERROR("attach error: %s", snd_strerror(err)); | |
159 | snd_mixer_close(handle); | |
160 | return; | |
161 | } | |
162 | if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) { | |
163 | LOG_ERROR("register error: %s", snd_strerror(err)); | |
164 | snd_mixer_close(handle); | |
165 | return; | |
166 | } | |
167 | if ((err = snd_mixer_load(handle)) < 0) { | |
168 | LOG_ERROR("load error: %s", snd_strerror(err)); | |
169 | snd_mixer_close(handle); | |
170 | return; | |
171 | } | |
172 | ||
173 | snd_mixer_selem_id_alloca(&sid); | |
174 | ||
175 | snd_mixer_selem_id_set_index(sid, mixer_index); | |
176 | snd_mixer_selem_id_set_name(sid, mixer); | |
177 | ||
178 | if ((elem = snd_mixer_find_selem(handle, sid)) == NULL) { | |
179 | LOG_ERROR("error find selem %s", mixer); | |
180 | snd_mixer_close(handle); | |
181 | return; | |
182 | } | |
183 | ||
184 | if (snd_mixer_selem_has_playback_switch(elem)) { | |
185 | snd_mixer_selem_set_playback_switch_all(elem, 1); // unmute | |
186 | } | |
187 | ||
188 | err = snd_mixer_selem_get_playback_dB_range(elem, &min, &max); | |
189 | ||
190 | if (err < 0 || max - min < 1000) { | |
191 | // unable to get db range or range is less than 10dB - ignore and set using raw values | |
192 | if ((err = snd_mixer_selem_get_playback_volume_range(elem, &min, &max)) < 0) { | |
193 | LOG_ERROR("unable to get volume raw range"); | |
194 | } else { | |
195 | long lraw, rraw; | |
196 | if (setmax) { | |
197 | lraw = rraw = max; | |
198 | } else { | |
199 | lraw = ((ldB > -MINVOL_DB ? MINVOL_DB + floor(ldB) : 0) / MINVOL_DB * (max-min)) + min; | |
200 | rraw = ((rdB > -MINVOL_DB ? MINVOL_DB + floor(rdB) : 0) / MINVOL_DB * (max-min)) + min; | |
201 | } | |
202 | LOG_DEBUG("setting vol raw [%ld..%ld]", min, max); | |
203 | if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lraw)) < 0) { | |
204 | LOG_ERROR("error setting left volume: %s", snd_strerror(err)); | |
205 | } | |
206 | if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rraw)) < 0) { | |
207 | LOG_ERROR("error setting right volume: %s", snd_strerror(err)); | |
208 | } | |
209 | } | |
210 | } else { | |
211 | // set db directly | |
212 | LOG_DEBUG("setting vol dB [%ld..%ld]", min, max); | |
213 | if (setmax) { | |
214 | // set to 0dB if available as this should be max volume for music recored at max pcm values | |
215 | if (max >= 0 && min <= 0) { | |
216 | ldB = rdB = 0; | |
217 | } else { | |
218 | ldB = rdB = max; | |
219 | } | |
220 | } | |
221 | if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_LEFT, 100 * ldB, 1)) < 0) { | |
222 | LOG_ERROR("error setting left volume: %s", snd_strerror(err)); | |
223 | } | |
224 | if ((err = snd_mixer_selem_set_playback_dB(elem, SND_MIXER_SCHN_FRONT_RIGHT, 100 * rdB, 1)) < 0) { | |
225 | LOG_ERROR("error setting right volume: %s", snd_strerror(err)); | |
226 | } | |
227 | } | |
228 | ||
229 | if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &nleft)) < 0) { | |
230 | LOG_ERROR("error getting left vol: %s", snd_strerror(err)); | |
231 | } | |
232 | if ((err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &nright)) < 0) { | |
233 | LOG_ERROR("error getting right vol: %s", snd_strerror(err)); | |
234 | } | |
235 | ||
236 | LOG_DEBUG("%s left: %3.1fdB -> %ld right: %3.1fdB -> %ld", mixer, ldB, nleft, rdB, nright); | |
237 | ||
238 | snd_mixer_close(handle); | |
239 | } | |
240 | ||
241 | void set_volume(unsigned left, unsigned right) { | |
242 | float ldB, rdB; | |
243 | ||
244 | if (!alsa.volume_mixer_name) { | |
245 | LOG_DEBUG("setting internal gain left: %u right: %u", left, right); | |
246 | LOCK; | |
247 | output.gainL = left; | |
248 | output.gainR = right; | |
249 | UNLOCK; | |
250 | return; | |
251 | } else { | |
252 | LOCK; | |
253 | output.gainL = FIXED_ONE; | |
254 | output.gainR = FIXED_ONE; | |
255 | UNLOCK; | |
256 | } | |
257 | ||
258 | // convert 16.16 fixed point to dB | |
259 | ldB = 20 * log10( left / 65536.0F ); | |
260 | rdB = 20 * log10( right / 65536.0F ); | |
261 | ||
262 | set_mixer(output.device, alsa.volume_mixer_name, alsa.volume_mixer_index, false, ldB, rdB); | |
263 | } | |
264 | ||
265 | static void *alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) { | |
266 | va_list args; | |
267 | if ((loglevel >= lINFO && err == 0) || loglevel >= lDEBUG) { | |
268 | fprintf(stderr, "%s ALSA %s:%d ", logtime(), function, line); | |
269 | va_start(args, fmt); | |
270 | vfprintf(stderr, fmt, args); | |
271 | fprintf(stderr, "\n"); | |
272 | fflush(stderr); | |
273 | } | |
274 | return NULL; | |
275 | } | |
276 | ||
277 | static void alsa_close(void) { | |
278 | int err; | |
279 | if ((err = snd_pcm_close(pcmp)) < 0) { | |
280 | LOG_INFO("snd_pcm_close error: %s", snd_strerror(err)); | |
281 | } | |
282 | } | |
283 | ||
284 | bool test_open(const char *device, unsigned rates[]) { | |
285 | int err; | |
286 | snd_pcm_t *pcm; | |
287 | snd_pcm_hw_params_t *hw_params; | |
288 | hw_params = (snd_pcm_hw_params_t *) alloca(snd_pcm_hw_params_sizeof()); | |
289 | memset(hw_params, 0, snd_pcm_hw_params_sizeof()); | |
290 | ||
291 | // open device | |
292 | if ((err = snd_pcm_open(&pcm, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { | |
293 | LOG_ERROR("playback open error: %s", snd_strerror(err)); | |
294 | return false; | |
295 | } | |
296 | ||
297 | // get max params | |
298 | if ((err = snd_pcm_hw_params_any(pcm, hw_params)) < 0) { | |
299 | LOG_ERROR("hwparam init error: %s", snd_strerror(err)); | |
300 | return false; | |
301 | } | |
302 | ||
303 | // find supported sample rates to enable client side resampling of non supported rates | |
304 | unsigned i, ind; | |
305 | unsigned ref[] TEST_RATES; | |
306 | ||
307 | for (i = 0, ind = 0; ref[i]; ++i) { | |
308 | if (snd_pcm_hw_params_test_rate(pcm, hw_params, ref[i], 0) == 0) { | |
309 | rates[ind++] = ref[i]; | |
310 | } | |
311 | } | |
312 | ||
313 | if ((err = snd_pcm_close(pcm)) < 0) { | |
314 | LOG_ERROR("snd_pcm_close error: %s", snd_strerror(err)); | |
315 | return false; | |
316 | } | |
317 | ||
318 | return true; | |
319 | } | |
320 | ||
321 | static bool pcm_probe(const char *device) { | |
322 | int err; | |
323 | snd_pcm_t *pcm; | |
324 | ||
325 | if ((err = snd_pcm_open(&pcm, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { | |
326 | return false; | |
327 | } | |
328 | ||
329 | if ((err = snd_pcm_close(pcm)) < 0) { | |
330 | LOG_ERROR("snd_pcm_close error: %s", snd_strerror(err)); | |
331 | } | |
332 | ||
333 | return true; | |
334 | } | |
335 | ||
336 | static int alsa_open(const char *device, unsigned sample_rate, unsigned alsa_buffer, unsigned alsa_period) { | |
337 | int err; | |
338 | snd_pcm_hw_params_t *hw_params; | |
339 | snd_pcm_hw_params_alloca(&hw_params); | |
340 | ||
341 | // close if already open | |
342 | if (pcmp) alsa_close(); | |
343 | ||
344 | // reset params | |
345 | alsa.rate = 0; | |
346 | alsa.period_size = 0; | |
347 | strcpy(alsa.device, device); | |
348 | ||
349 | if (strlen(device) > MAX_DEVICE_LEN - 4 - 1) { | |
350 | LOG_ERROR("device name too long: %s", device); | |
351 | return -1; | |
352 | } | |
353 | ||
354 | LOG_INFO("opening device at: %u", sample_rate); | |
355 | ||
356 | bool retry; | |
357 | do { | |
358 | // open device | |
359 | if ((err = snd_pcm_open(&pcmp, alsa.device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { | |
360 | LOG_ERROR("playback open error: %s", snd_strerror(err)); | |
361 | return err; | |
362 | } | |
363 | ||
364 | // init params | |
365 | memset(hw_params, 0, snd_pcm_hw_params_sizeof()); | |
366 | if ((err = snd_pcm_hw_params_any(pcmp, hw_params)) < 0) { | |
367 | LOG_ERROR("hwparam init error: %s", snd_strerror(err)); | |
368 | return err; | |
369 | } | |
370 | ||
371 | // open hw: devices without resampling, if sample rate fails try plughw: with resampling | |
372 | bool hw = !strncmp(alsa.device, "hw:", 3); | |
373 | retry = false; | |
374 | ||
375 | if ((err = snd_pcm_hw_params_set_rate_resample(pcmp, hw_params, !hw)) < 0) { | |
376 | LOG_ERROR("resampling setup failed: %s", snd_strerror(err)); | |
377 | return err; | |
378 | } | |
379 | ||
380 | if ((err = snd_pcm_hw_params_set_rate(pcmp, hw_params, sample_rate, 0)) < 0) { | |
381 | if (hw) { | |
382 | strcpy(alsa.device + 4, device); | |
383 | memcpy(alsa.device, "plug", 4); | |
384 | LOG_INFO("reopening device %s in plug mode as %s for resampling", device, alsa.device); | |
385 | snd_pcm_close(pcmp); | |
386 | retry = true; | |
387 | } | |
388 | } | |
389 | ||
390 | } while (retry); | |
391 | ||
392 | // set access | |
393 | if (!alsa.mmap || snd_pcm_hw_params_set_access(pcmp, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) { | |
394 | if ((err = snd_pcm_hw_params_set_access(pcmp, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { | |
395 | LOG_ERROR("access type not available: %s", snd_strerror(err)); | |
396 | return err; | |
397 | } | |
398 | alsa.mmap = false; | |
399 | } | |
400 | ||
401 | // set the sample format | |
402 | snd_pcm_format_t *fmt = alsa.format ? &alsa.format : (snd_pcm_format_t *)fmts; | |
403 | do { | |
404 | if (snd_pcm_hw_params_set_format(pcmp, hw_params, *fmt) >= 0) { | |
405 | LOG_INFO("opened device %s using format: %s sample rate: %u mmap: %u", alsa.device, snd_pcm_format_name(*fmt), sample_rate, alsa.mmap); | |
406 | alsa.format = *fmt; | |
407 | break; | |
408 | } | |
409 | if (alsa.format) { | |
410 | LOG_ERROR("unable to open audio device requested format: %s", snd_pcm_format_name(alsa.format)); | |
411 | return -1; | |
412 | } | |
413 | ++fmt; | |
414 | if (*fmt == SND_PCM_FORMAT_UNKNOWN) { | |
415 | LOG_ERROR("unable to open audio device with any supported format"); | |
416 | return -1; | |
417 | } | |
418 | } while (*fmt != SND_PCM_FORMAT_UNKNOWN); | |
419 | ||
420 | // set the output format to be used by _scale_and_pack | |
421 | switch(alsa.format) { | |
422 | case SND_PCM_FORMAT_S32_LE: | |
423 | output.format = S32_LE; break; | |
424 | case SND_PCM_FORMAT_S24_LE: | |
425 | output.format = S24_LE; break; | |
426 | case SND_PCM_FORMAT_S24_3LE: | |
427 | output.format = S24_3LE; break; | |
428 | case SND_PCM_FORMAT_S16_LE: | |
429 | output.format = S16_LE; break; | |
430 | default: | |
431 | break; | |
432 | } | |
433 | ||
434 | // set channels | |
435 | if ((err = snd_pcm_hw_params_set_channels (pcmp, hw_params, 2)) < 0) { | |
436 | LOG_ERROR("channel count not available: %s", snd_strerror(err)); | |
437 | return err; | |
438 | } | |
439 | ||
440 | // set period size - value of < 50 treated as period count, otherwise size in bytes | |
441 | if (alsa_period < 50) { | |
442 | unsigned count = alsa_period; | |
443 | if ((err = snd_pcm_hw_params_set_periods_near(pcmp, hw_params, &count, 0)) < 0) { | |
444 | LOG_ERROR("unable to set period count %s", snd_strerror(err)); | |
445 | return err; | |
446 | } | |
447 | } else { | |
448 | snd_pcm_uframes_t size = alsa_period; | |
449 | int dir = 0; | |
450 | if ((err = snd_pcm_hw_params_set_period_size_near(pcmp, hw_params, &size, &dir)) < 0) { | |
451 | LOG_ERROR("unable to set period size %s", snd_strerror(err)); | |
452 | return err; | |
453 | } | |
454 | } | |
455 | ||
456 | // set buffer size - value of < 500 treated as buffer time in ms, otherwise size in bytes | |
457 | if (alsa_buffer < 500) { | |
458 | unsigned time = alsa_buffer * 1000; | |
459 | int dir = 0; | |
460 | if ((err = snd_pcm_hw_params_set_buffer_time_near(pcmp, hw_params, &time, &dir)) < 0) { | |
461 | LOG_ERROR("unable to set buffer time %s", snd_strerror(err)); | |
462 | return err; | |
463 | } | |
464 | } else { | |
465 | snd_pcm_uframes_t size = alsa_buffer; | |
466 | if ((err = snd_pcm_hw_params_set_buffer_size_near(pcmp, hw_params, &size)) < 0) { | |
467 | LOG_ERROR("unable to set buffer size %s", snd_strerror(err)); | |
468 | return err; | |
469 | } | |
470 | } | |
471 | ||
472 | // get period_size | |
473 | if ((err = snd_pcm_hw_params_get_period_size(hw_params, &alsa.period_size, 0)) < 0) { | |
474 | LOG_ERROR("unable to get period size: %s", snd_strerror(err)); | |
475 | return err; | |
476 | } | |
477 | ||
478 | // get buffer_size | |
479 | if ((err = snd_pcm_hw_params_get_buffer_size(hw_params, &alsa.buffer_size)) < 0) { | |
480 | LOG_ERROR("unable to get buffer size: %s", snd_strerror(err)); | |
481 | return err; | |
482 | } | |
483 | ||
484 | LOG_INFO("buffer: %u period: %u -> buffer size: %u period size: %u", alsa_buffer, alsa_period, alsa.buffer_size, alsa.period_size); | |
485 | ||
486 | // ensure we have two buffer sizes of samples before starting output | |
487 | output.start_frames = alsa.buffer_size * 2; | |
488 | ||
489 | // create an intermediate buffer for non mmap case for all but NATIVE_FORMAT | |
490 | // this is used to pack samples into the output format before calling writei | |
491 | if (!alsa.mmap && !alsa.write_buf && alsa.format != NATIVE_FORMAT) { | |
492 | alsa.write_buf = malloc(alsa.buffer_size * BYTES_PER_FRAME); | |
493 | if (!alsa.write_buf) { | |
494 | LOG_ERROR("unable to malloc write_buf"); | |
495 | return -1; | |
496 | } | |
497 | } | |
498 | ||
499 | // set params | |
500 | if ((err = snd_pcm_hw_params(pcmp, hw_params)) < 0) { | |
501 | LOG_ERROR("unable to set hw params: %s", snd_strerror(err)); | |
502 | return err; | |
503 | } | |
504 | ||
505 | // dump info | |
506 | if (loglevel == lSDEBUG) { | |
507 | static snd_output_t *debug_output; | |
508 | snd_output_stdio_attach(&debug_output, stderr, 0); | |
509 | snd_pcm_dump(pcmp, debug_output); | |
510 | } | |
511 | ||
512 | // this indicates we have opened the device ok | |
513 | alsa.rate = sample_rate; | |
514 | ||
515 | return 0; | |
516 | } | |
517 | ||
518 | static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, | |
519 | s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) { | |
520 | ||
521 | const snd_pcm_channel_area_t *areas; | |
522 | snd_pcm_uframes_t offset; | |
523 | void *outputptr; | |
524 | s32_t *inputptr; | |
525 | int err; | |
526 | ||
527 | if (alsa.mmap) { | |
528 | snd_pcm_uframes_t alsa_frames = (snd_pcm_uframes_t)out_frames; | |
529 | ||
530 | snd_pcm_avail_update(pcmp); | |
531 | ||
532 | if ((err = snd_pcm_mmap_begin(pcmp, &areas, &offset, &alsa_frames)) < 0) { | |
533 | LOG_WARN("error from mmap_begin: %s", snd_strerror(err)); | |
534 | return -1; | |
535 | } | |
536 | ||
537 | out_frames = (frames_t)alsa_frames; | |
538 | } | |
539 | ||
540 | if (!silence) { | |
541 | // applying cross fade is delayed until this point as mmap_begin can change out_frames | |
542 | if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) { | |
543 | _apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr); | |
544 | } | |
545 | } | |
546 | ||
547 | inputptr = (s32_t *) (silence ? silencebuf : outputbuf->readp); | |
548 | ||
549 | IF_DSD( | |
550 | if (output.dop) { | |
551 | if (silence) { | |
552 | inputptr = (s32_t *) silencebuf_dop; | |
553 | } | |
554 | update_dop((u32_t *) inputptr, out_frames, output.invert && !silence); | |
555 | } | |
556 | ) | |
557 | ||
558 | if (alsa.mmap || alsa.format != NATIVE_FORMAT) { | |
559 | ||
560 | outputptr = alsa.mmap ? (areas[0].addr + (areas[0].first + offset * areas[0].step) / 8) : alsa.write_buf; | |
561 | ||
562 | _scale_and_pack_frames(outputptr, inputptr, out_frames, gainL, gainR, output.format); | |
563 | ||
564 | } else { | |
565 | ||
566 | outputptr = (void *)inputptr; | |
567 | ||
568 | if (!silence) { | |
569 | ||
570 | if (gainL != FIXED_ONE || gainR!= FIXED_ONE) { | |
571 | _apply_gain(outputbuf, out_frames, gainL, gainR); | |
572 | } | |
573 | } | |
574 | } | |
575 | ||
576 | if (alsa.mmap) { | |
577 | ||
578 | snd_pcm_sframes_t w = snd_pcm_mmap_commit(pcmp, offset, out_frames); | |
579 | if (w < 0 || w != out_frames) { | |
580 | LOG_WARN("mmap_commit error"); | |
581 | return -1; | |
582 | } | |
583 | ||
584 | } else { | |
585 | ||
586 | snd_pcm_sframes_t w = snd_pcm_writei(pcmp, outputptr, out_frames); | |
587 | if (w < 0) { | |
588 | //if (w != -EAGAIN && ((err = snd_pcm_recover(pcmp, w, 1)) < 0)) { | |
589 | if (((err = snd_pcm_recover(pcmp, w, 1)) < 0)) { | |
590 | static unsigned recover_count = 0; | |
591 | LOG_WARN("recover failed: %s [%u]", snd_strerror(err), ++recover_count); | |
592 | if (recover_count >= 10) { | |
593 | recover_count = 0; | |
594 | alsa_close(); | |
595 | pcmp = NULL; | |
596 | } | |
597 | } | |
598 | return -1; | |
599 | } else { | |
600 | if (w != out_frames) { | |
601 | LOG_WARN("writei only wrote %u of %u", w, out_frames); | |
602 | } | |
603 | out_frames = w; | |
604 | } | |
605 | } | |
606 | ||
607 | return (int)out_frames; | |
608 | } | |
609 | ||
610 | static void *output_thread(void *arg) { | |
611 | bool start = true; | |
612 | bool output_off = (output.state == OUTPUT_OFF); | |
613 | bool probe_device = (arg != NULL); | |
614 | int err; | |
615 | ||
616 | while (running) { | |
617 | ||
618 | // disabled output - player is off | |
619 | while (output_off) { | |
620 | usleep(100000); | |
621 | LOCK; | |
622 | output_off = (output.state == OUTPUT_OFF); | |
623 | UNLOCK; | |
624 | if (!running) return 0; | |
625 | } | |
626 | ||
627 | // wait until device returns - to allow usb audio devices to be turned off | |
628 | if (probe_device) { | |
629 | while (!pcm_probe(output.device)) { | |
630 | LOG_DEBUG("waiting for device %s to return", output.device); | |
631 | sleep(5); | |
632 | } | |
633 | probe_device = false; | |
634 | } | |
635 | ||
636 | if (!pcmp || alsa.rate != output.current_sample_rate) { | |
637 | LOG_INFO("open output device: %s", output.device); | |
638 | LOCK; | |
639 | ||
640 | // FIXME - some alsa hardware requires opening twice for a new sample rate to work | |
641 | // this is a workaround which should be removed | |
642 | if (alsa.reopen) { | |
643 | alsa_open(output.device, output.current_sample_rate, output.buffer, output.period); | |
644 | } | |
645 | ||
646 | if (!!alsa_open(output.device, output.current_sample_rate, output.buffer, output.period)) { | |
647 | output.error_opening = true; | |
648 | UNLOCK; | |
649 | sleep(5); | |
650 | continue; | |
651 | } | |
652 | output.error_opening = false; | |
653 | start = true; | |
654 | UNLOCK; | |
655 | } | |
656 | ||
657 | snd_pcm_state_t state = snd_pcm_state(pcmp); | |
658 | ||
659 | if (state == SND_PCM_STATE_XRUN) { | |
660 | LOG_INFO("XRUN"); | |
661 | if ((err = snd_pcm_recover(pcmp, -EPIPE, 1)) < 0) { | |
662 | LOG_INFO("XRUN recover failed: %s", snd_strerror(err)); | |
663 | } | |
664 | start = true; | |
665 | continue; | |
666 | } else if (state == SND_PCM_STATE_SUSPENDED) { | |
667 | if ((err = snd_pcm_recover(pcmp, -ESTRPIPE, 1)) < 0) { | |
668 | LOG_INFO("SUSPEND recover failed: %s", snd_strerror(err)); | |
669 | } | |
670 | } else if (state == SND_PCM_STATE_DISCONNECTED) { | |
671 | LOG_INFO("Device %s no longer available", output.device); | |
672 | alsa_close(); | |
673 | pcmp = NULL; | |
674 | probe_device = true; | |
675 | continue; | |
676 | } | |
677 | ||
678 | snd_pcm_sframes_t avail = snd_pcm_avail_update(pcmp); | |
679 | ||
680 | if (avail < 0) { | |
681 | if ((err = snd_pcm_recover(pcmp, avail, 1)) < 0) { | |
682 | if (err == -ENODEV) { | |
683 | LOG_INFO("Device %s no longer available", output.device); | |
684 | alsa_close(); | |
685 | pcmp = NULL; | |
686 | probe_device = true; | |
687 | continue; | |
688 | } | |
689 | LOG_WARN("recover failed: %s", snd_strerror(err)); | |
690 | } | |
691 | start = true; | |
692 | continue; | |
693 | } | |
694 | ||
695 | if (avail < alsa.period_size) { | |
696 | if (start) { | |
697 | if (alsa.mmap && ((err = snd_pcm_start(pcmp)) < 0)) { | |
698 | if ((err = snd_pcm_recover(pcmp, err, 1)) < 0) { | |
699 | if (err == -ENODEV) { | |
700 | LOG_INFO("Device %s no longer available", output.device); | |
701 | alsa_close(); | |
702 | pcmp = NULL; | |
703 | probe_device = true; | |
704 | continue; | |
705 | } | |
706 | LOG_INFO("start error: %s", snd_strerror(err)); | |
707 | usleep(10000); | |
708 | } | |
709 | } else { | |
710 | start = false; | |
711 | } | |
712 | } else { | |
713 | if ((err = snd_pcm_wait(pcmp, 1000)) < 0) { | |
714 | if ((err = snd_pcm_recover(pcmp, err, 1)) < 0) { | |
715 | LOG_INFO("pcm wait error: %s", snd_strerror(err)); | |
716 | } | |
717 | start = true; | |
718 | } | |
719 | } | |
720 | continue; | |
721 | } | |
722 | ||
723 | // restrict avail to within sensible limits as alsa drivers can return erroneous large values | |
724 | // in writei mode restrict to period_size due to size of write_buf | |
725 | if (alsa.mmap) { | |
726 | avail = min(avail, alsa.buffer_size); | |
727 | } else { | |
728 | avail = min(avail, alsa.period_size); | |
729 | } | |
730 | ||
731 | // avoid spinning in cases where wait returns but no bytes available (seen with pulse audio) | |
732 | if (avail == 0) { | |
733 | LOG_SDEBUG("avail 0 - sleeping"); | |
734 | usleep(10000); | |
735 | continue; | |
736 | } | |
737 | ||
738 | LOCK; | |
739 | ||
740 | // turn off if requested | |
741 | if (output.state == OUTPUT_OFF) { | |
742 | UNLOCK; | |
743 | LOG_INFO("disabling output"); | |
744 | alsa_close(); | |
745 | pcmp = NULL; | |
746 | output_off = true; | |
747 | vis_stop(); | |
748 | continue; | |
749 | } | |
750 | ||
751 | // measure output delay | |
752 | snd_pcm_sframes_t delay; | |
753 | if ((err = snd_pcm_delay(pcmp, &delay)) < 0) { | |
754 | if (err == -EPIPE) { | |
755 | // EPIPE indicates underrun - attempt to recover | |
756 | UNLOCK; | |
757 | continue; | |
758 | } else if (err == -EIO) { | |
759 | // EIO can occur with non existant pulse server | |
760 | UNLOCK; | |
761 | LOG_SDEBUG("snd_pcm_delay returns: EIO - sleeping"); | |
762 | usleep(100000); | |
763 | continue; | |
764 | } else { | |
765 | LOG_DEBUG("snd_pcm_delay returns: %d", err); | |
766 | } | |
767 | } else { | |
768 | output.device_frames = delay; | |
769 | output.updated = gettime_ms(); | |
770 | output.frames_played_dmp = output.frames_played; | |
771 | } | |
772 | ||
773 | // process frames | |
774 | frames_t wrote = _output_frames(avail); | |
775 | ||
776 | UNLOCK; | |
777 | ||
778 | // some output devices such as alsa null refuse any data, avoid spinning | |
779 | if (!wrote) { | |
780 | LOG_SDEBUG("wrote 0 - sleeping"); | |
781 | usleep(10000); | |
782 | } | |
783 | } | |
784 | ||
785 | return 0; | |
786 | } | |
787 | ||
788 | static pthread_t thread; | |
789 | ||
790 | void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], | |
791 | unsigned rate_delay, unsigned rt_priority, unsigned idle, char *volume_mixer, bool mixer_unmute) { | |
792 | ||
793 | unsigned alsa_buffer = ALSA_BUFFER_TIME; | |
794 | unsigned alsa_period = ALSA_PERIOD_COUNT; | |
795 | char *alsa_sample_fmt = NULL; | |
796 | bool alsa_mmap = true; | |
797 | bool alsa_reopen = false; | |
798 | ||
799 | char *volume_mixer_name = next_param(volume_mixer, ','); | |
800 | char *volume_mixer_index = next_param(NULL, ','); | |
801 | ||
802 | char *t = next_param(params, ':'); | |
803 | char *c = next_param(NULL, ':'); | |
804 | char *s = next_param(NULL, ':'); | |
805 | char *m = next_param(NULL, ':'); | |
806 | char *r = next_param(NULL, ':'); | |
807 | ||
808 | if (t) alsa_buffer = atoi(t); | |
809 | if (c) alsa_period = atoi(c); | |
810 | if (s) alsa_sample_fmt = s; | |
811 | if (m) alsa_mmap = atoi(m); | |
812 | if (r) alsa_reopen = atoi(r); | |
813 | ||
814 | loglevel = level; | |
815 | ||
816 | LOG_INFO("init output"); | |
817 | ||
818 | memset(&output, 0, sizeof(output)); | |
819 | ||
820 | alsa.mmap = alsa_mmap; | |
821 | alsa.write_buf = NULL; | |
822 | alsa.format = 0; | |
823 | alsa.reopen = alsa_reopen; | |
824 | ||
825 | if (!mixer_unmute) { | |
826 | alsa.volume_mixer_name = volume_mixer_name; | |
827 | alsa.volume_mixer_index = volume_mixer_index ? atoi(volume_mixer_index) : 0; | |
828 | } | |
829 | ||
830 | output.format = 0; | |
831 | output.buffer = alsa_buffer; | |
832 | output.period = alsa_period; | |
833 | output.start_frames = 0; | |
834 | output.write_cb = &_write_frames; | |
835 | output.rate_delay = rate_delay; | |
836 | ||
837 | if (alsa_sample_fmt) { | |
838 | if (!strcmp(alsa_sample_fmt, "32")) alsa.format = SND_PCM_FORMAT_S32_LE; | |
839 | if (!strcmp(alsa_sample_fmt, "24")) alsa.format = SND_PCM_FORMAT_S24_LE; | |
840 | if (!strcmp(alsa_sample_fmt, "24_3")) alsa.format = SND_PCM_FORMAT_S24_3LE; | |
841 | if (!strcmp(alsa_sample_fmt, "16")) alsa.format = SND_PCM_FORMAT_S16_LE; | |
842 | } | |
843 | ||
844 | LOG_INFO("requested alsa_buffer: %u alsa_period: %u format: %s mmap: %u", output.buffer, output.period, | |
845 | alsa_sample_fmt ? alsa_sample_fmt : "any", alsa.mmap); | |
846 | ||
847 | snd_lib_error_set_handler((snd_lib_error_handler_t)alsa_error_handler); | |
848 | ||
849 | output_init_common(level, device, output_buf_size, rates, idle); | |
850 | ||
851 | if (mixer_unmute && volume_mixer_name) { | |
852 | set_mixer(output.device, volume_mixer_name, volume_mixer_index ? atoi(volume_mixer_index) : 0, true, 0, 0); | |
853 | } | |
854 | ||
855 | #if LINUX | |
856 | // RT linux - aim to avoid pagefaults by locking memory: | |
857 | // https://rt.wiki.kernel.org/index.php/Threaded_RT-application_with_memory_locking_and_stack_handling_example | |
858 | if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) { | |
859 | LOG_INFO("unable to lock memory: %s", strerror(errno)); | |
860 | } else { | |
861 | LOG_INFO("memory locked"); | |
862 | } | |
863 | ||
864 | mallopt(M_TRIM_THRESHOLD, -1); | |
865 | mallopt(M_MMAP_MAX, 0); | |
866 | ||
867 | touch_memory(silencebuf, MAX_SILENCE_FRAMES * BYTES_PER_FRAME); | |
868 | touch_memory(outputbuf->buf, outputbuf->size); | |
869 | #endif | |
870 | ||
871 | // start output thread | |
872 | pthread_attr_t attr; | |
873 | pthread_attr_init(&attr); | |
874 | pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE); | |
875 | pthread_create(&thread, &attr, output_thread, rates[0] ? "probe" : NULL); | |
876 | pthread_attr_destroy(&attr); | |
877 | ||
878 | // try to set this thread to real-time scheduler class, only works as root or if user has permission | |
879 | struct sched_param param; | |
880 | param.sched_priority = rt_priority; | |
881 | if (pthread_setschedparam(thread, SCHED_FIFO, ¶m) != 0) { | |
882 | LOG_DEBUG("unable to set output sched fifo: %s", strerror(errno)); | |
883 | } else { | |
884 | LOG_DEBUG("set output sched fifo rt: %u", param.sched_priority); | |
885 | } | |
886 | } | |
887 | ||
888 | void output_close_alsa(void) { | |
889 | LOG_INFO("close output"); | |
890 | ||
891 | LOCK; | |
892 | running = false; | |
893 | UNLOCK; | |
894 | ||
895 | pthread_join(thread, NULL); | |
896 | ||
897 | if (alsa.write_buf) free(alsa.write_buf); | |
898 | ||
899 | output_close_common(); | |
900 | } | |
901 | ||
902 | #endif // ALSA | |
903 |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // Portaudio output | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #if PORTAUDIO | |
25 | ||
26 | #include <portaudio.h> | |
27 | #if OSX | |
28 | #include <pa_mac_core.h> | |
29 | #endif | |
30 | ||
31 | // ouput device | |
32 | static struct { | |
33 | unsigned rate; | |
34 | PaStream *stream; | |
35 | } pa; | |
36 | ||
37 | static log_level loglevel; | |
38 | ||
39 | static bool running = true; | |
40 | ||
41 | extern struct outputstate output; | |
42 | extern struct buffer *outputbuf; | |
43 | ||
44 | #define LOCK mutex_lock(outputbuf->mutex) | |
45 | #define UNLOCK mutex_unlock(outputbuf->mutex) | |
46 | ||
47 | extern u8_t *silencebuf; | |
48 | #if DSD | |
49 | extern u8_t *silencebuf_dop; | |
50 | #endif | |
51 | ||
52 | void list_devices(void) { | |
53 | PaError err; | |
54 | int i; | |
55 | ||
56 | if ((err = Pa_Initialize()) != paNoError) { | |
57 | LOG_WARN("error initialising port audio: %s", Pa_GetErrorText(err)); | |
58 | return; | |
59 | } | |
60 | ||
61 | printf("Output devices:\n"); | |
62 | for (i = 0; i < Pa_GetDeviceCount(); ++i) { | |
63 | if (Pa_GetDeviceInfo(i)->maxOutputChannels) { | |
64 | printf(" %i - %s [%s]\n", i, Pa_GetDeviceInfo(i)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(i)->hostApi)->name); | |
65 | } | |
66 | } | |
67 | printf("\n"); | |
68 | ||
69 | if ((err = Pa_Terminate()) != paNoError) { | |
70 | LOG_WARN("error closing port audio: %s", Pa_GetErrorText(err)); | |
71 | } | |
72 | } | |
73 | ||
74 | void set_volume(unsigned left, unsigned right) { | |
75 | LOG_DEBUG("setting internal gain left: %u right: %u", left, right); | |
76 | LOCK; | |
77 | output.gainL = left; | |
78 | output.gainR = right; | |
79 | UNLOCK; | |
80 | } | |
81 | ||
82 | static int pa_device_id(const char *device) { | |
83 | int len = strlen(device); | |
84 | int i; | |
85 | ||
86 | if (!strncmp(device, "default", 7)) { | |
87 | return Pa_GetDefaultOutputDevice(); | |
88 | } | |
89 | if (len >= 1 && len <= 2 && device[0] >= '0' && device[0] <= '9') { | |
90 | return atoi(device); | |
91 | } | |
92 | ||
93 | #define DEVICE_ID_MAXLEN 256 | |
94 | for (i = 0; i < Pa_GetDeviceCount(); ++i) { | |
95 | char tmp[DEVICE_ID_MAXLEN]; | |
96 | snprintf(tmp, DEVICE_ID_MAXLEN, "%s [%s]", Pa_GetDeviceInfo(i)->name, Pa_GetHostApiInfo(Pa_GetDeviceInfo(i)->hostApi)->name); | |
97 | if (!strncmp(tmp, device, len)) { | |
98 | return i; | |
99 | } | |
100 | } | |
101 | ||
102 | return -1; | |
103 | } | |
104 | ||
105 | static int pa_callback(const void *pa_input, void *pa_output, unsigned long pa_frames_wanted, | |
106 | const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData); | |
107 | ||
108 | bool test_open(const char *device, unsigned rates[]) { | |
109 | PaStreamParameters outputParameters; | |
110 | PaError err; | |
111 | unsigned ref[] TEST_RATES; | |
112 | int device_id, i, ind; | |
113 | ||
114 | if ((device_id = pa_device_id(device)) == -1) { | |
115 | LOG_INFO("device %s not found", device); | |
116 | return false; | |
117 | } | |
118 | ||
119 | outputParameters.device = device_id; | |
120 | outputParameters.channelCount = 2; | |
121 | outputParameters.sampleFormat = paInt32; | |
122 | outputParameters.suggestedLatency = | |
123 | output.latency ? (double)output.latency/(double)1000 : Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency; | |
124 | outputParameters.hostApiSpecificStreamInfo = NULL; | |
125 | ||
126 | // check supported sample rates | |
127 | // Note use Pa_OpenStream as it appears more reliable than Pa_IsFormatSupported on some windows apis | |
128 | for (i = 0, ind = 0; ref[i]; ++i) { | |
129 | err = Pa_OpenStream(&pa.stream, NULL, &outputParameters, (double)ref[i], paFramesPerBufferUnspecified, paNoFlag, | |
130 | pa_callback, NULL); | |
131 | if (err == paNoError) { | |
132 | Pa_CloseStream(pa.stream); | |
133 | rates[ind++] = ref[i]; | |
134 | } | |
135 | } | |
136 | ||
137 | if (!rates[0]) { | |
138 | LOG_WARN("no available rate found"); | |
139 | return false; | |
140 | } | |
141 | ||
142 | pa.stream = NULL; | |
143 | return true; | |
144 | } | |
145 | ||
146 | static void pa_stream_finished(void *userdata) { | |
147 | if (running) { | |
148 | LOG_INFO("stream finished"); | |
149 | LOCK; | |
150 | output.pa_reopen = true; | |
151 | wake_controller(); | |
152 | UNLOCK; | |
153 | } | |
154 | } | |
155 | ||
156 | static thread_type monitor_thread; | |
157 | bool monitor_thread_running = false; | |
158 | ||
159 | static void *pa_monitor() { | |
160 | bool output_off; | |
161 | ||
162 | LOCK; | |
163 | ||
164 | if (monitor_thread_running) { | |
165 | LOG_DEBUG("monitor thread already running"); | |
166 | UNLOCK; | |
167 | return 0; | |
168 | } | |
169 | ||
170 | LOG_DEBUG("start monitor thread"); | |
171 | ||
172 | monitor_thread_running = true; | |
173 | output_off = (output.state == OUTPUT_OFF); | |
174 | ||
175 | while (monitor_thread_running) { | |
176 | if (output_off) { | |
177 | if (output.state != OUTPUT_OFF) { | |
178 | LOG_INFO("output on"); | |
179 | break; | |
180 | } | |
181 | } else { | |
182 | // this is a hack to partially support hot plugging of devices | |
183 | // we rely on terminating and reinitalising PA to get an updated list of devices and use name for output.device | |
184 | LOG_INFO("probing device %s", output.device); | |
185 | Pa_Terminate(); | |
186 | Pa_Initialize(); | |
187 | pa.stream = NULL; | |
188 | if (pa_device_id(output.device) != -1) { | |
189 | LOG_INFO("device reopen"); | |
190 | break; | |
191 | } | |
192 | } | |
193 | ||
194 | UNLOCK; | |
195 | sleep(output_off ? 1 : 5); | |
196 | LOCK; | |
197 | } | |
198 | ||
199 | LOG_DEBUG("end monitor thread"); | |
200 | ||
201 | monitor_thread_running = false; | |
202 | pa.stream = NULL; | |
203 | ||
204 | _pa_open(); | |
205 | ||
206 | UNLOCK; | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | void _pa_open(void) { | |
212 | PaStreamParameters outputParameters; | |
213 | PaError err = paNoError; | |
214 | int device_id; | |
215 | ||
216 | if (pa.stream) { | |
217 | if ((err = Pa_CloseStream(pa.stream)) != paNoError) { | |
218 | LOG_WARN("error closing stream: %s", Pa_GetErrorText(err)); | |
219 | } | |
220 | } | |
221 | ||
222 | if (output.state == OUTPUT_OFF) { | |
223 | // we get called when transitioning to OUTPUT_OFF to create the probe thread | |
224 | // set err to avoid opening device and logging messages | |
225 | err = 1; | |
226 | ||
227 | } else if ((device_id = pa_device_id(output.device)) == -1) { | |
228 | LOG_INFO("device %s not found", output.device); | |
229 | err = 1; | |
230 | ||
231 | } else { | |
232 | ||
233 | outputParameters.device = device_id; | |
234 | outputParameters.channelCount = 2; | |
235 | outputParameters.sampleFormat = paInt32; | |
236 | outputParameters.suggestedLatency = | |
237 | output.latency ? (double)output.latency/(double)1000 : Pa_GetDeviceInfo(outputParameters.device)->defaultHighOutputLatency; | |
238 | outputParameters.hostApiSpecificStreamInfo = NULL; | |
239 | ||
240 | #if OSX | |
241 | // enable pro mode which aims to avoid resampling if possible | |
242 | // see http://code.google.com/p/squeezelite/issues/detail?id=11 & http://code.google.com/p/squeezelite/issues/detail?id=37 | |
243 | // command line controls osx_playnice which is -1 if not specified, 0 or 1 - choose playnice if -1 or 1 | |
244 | PaMacCoreStreamInfo macInfo; | |
245 | unsigned long streamInfoFlags; | |
246 | if (output.osx_playnice) { | |
247 | LOG_INFO("opening device in PlayNice mode"); | |
248 | streamInfoFlags = paMacCorePlayNice; | |
249 | } else { | |
250 | LOG_INFO("opening device in Pro mode"); | |
251 | streamInfoFlags = paMacCorePro; | |
252 | } | |
253 | PaMacCore_SetupStreamInfo(&macInfo, streamInfoFlags); | |
254 | outputParameters.hostApiSpecificStreamInfo = &macInfo; | |
255 | #endif | |
256 | } | |
257 | ||
258 | if (!err && | |
259 | (err = Pa_OpenStream(&pa.stream, NULL, &outputParameters, (double)output.current_sample_rate, paFramesPerBufferUnspecified, | |
260 | paPrimeOutputBuffersUsingStreamCallback | paDitherOff, pa_callback, NULL)) != paNoError) { | |
261 | LOG_WARN("error opening device %i - %s : %s", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name, | |
262 | Pa_GetErrorText(err)); | |
263 | } | |
264 | ||
265 | if (!err) { | |
266 | LOG_INFO("opened device %i - %s at %u latency %u ms", outputParameters.device, Pa_GetDeviceInfo(outputParameters.device)->name, | |
267 | (unsigned int)Pa_GetStreamInfo(pa.stream)->sampleRate, (unsigned int)(Pa_GetStreamInfo(pa.stream)->outputLatency * 1000)); | |
268 | ||
269 | pa.rate = output.current_sample_rate; | |
270 | ||
271 | if ((err = Pa_SetStreamFinishedCallback(pa.stream, pa_stream_finished)) != paNoError) { | |
272 | LOG_WARN("error setting finish callback: %s", Pa_GetErrorText(err)); | |
273 | } | |
274 | ||
275 | UNLOCK; // StartStream can call pa_callback in a sychronised thread on freebsd, remove lock while it is called | |
276 | ||
277 | if ((err = Pa_StartStream(pa.stream)) != paNoError) { | |
278 | LOG_WARN("error starting stream: %s", Pa_GetErrorText(err)); | |
279 | } | |
280 | ||
281 | LOCK; | |
282 | } | |
283 | ||
284 | if (err && !monitor_thread_running) { | |
285 | vis_stop(); | |
286 | ||
287 | // create a thread to check for output state change or device return | |
288 | #if LINUX || OSX || FREEBSD | |
289 | pthread_create(&monitor_thread, NULL, pa_monitor, NULL); | |
290 | #endif | |
291 | #if WIN | |
292 | monitor_thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&pa_monitor, NULL, 0, NULL); | |
293 | #endif | |
294 | } | |
295 | ||
296 | output.error_opening = !!err; | |
297 | } | |
298 | ||
299 | static u8_t *optr; | |
300 | ||
301 | static int _write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, | |
302 | s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) { | |
303 | ||
304 | if (!silence) { | |
305 | ||
306 | if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) { | |
307 | _apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr); | |
308 | } | |
309 | ||
310 | if (gainL != FIXED_ONE || gainR!= FIXED_ONE) { | |
311 | _apply_gain(outputbuf, out_frames, gainL, gainR); | |
312 | } | |
313 | ||
314 | IF_DSD( | |
315 | if (output.dop) { | |
316 | update_dop((u32_t *) outputbuf->readp, out_frames, output.invert); | |
317 | } | |
318 | ) | |
319 | ||
320 | memcpy(optr, outputbuf->readp, out_frames * BYTES_PER_FRAME); | |
321 | ||
322 | } else { | |
323 | ||
324 | u8_t *buf = silencebuf; | |
325 | ||
326 | IF_DSD( | |
327 | if (output.dop) { | |
328 | buf = silencebuf_dop; | |
329 | update_dop((u32_t *) buf, out_frames, false); // don't invert silence | |
330 | } | |
331 | ) | |
332 | ||
333 | memcpy(optr, buf, out_frames * BYTES_PER_FRAME); | |
334 | } | |
335 | ||
336 | optr += out_frames * BYTES_PER_FRAME; | |
337 | ||
338 | return (int)out_frames; | |
339 | } | |
340 | ||
341 | static int pa_callback(const void *pa_input, void *pa_output, unsigned long pa_frames_wanted, | |
342 | const PaStreamCallbackTimeInfo *time_info, PaStreamCallbackFlags statusFlags, void *userData) { | |
343 | int ret; | |
344 | double stream_time; | |
345 | frames_t frames; | |
346 | ||
347 | optr = (u8_t *)pa_output; | |
348 | ||
349 | LOCK; | |
350 | ||
351 | stream_time = Pa_GetStreamTime(pa.stream); | |
352 | ||
353 | if (time_info->outputBufferDacTime > stream_time) { | |
354 | // workaround for wdm-ks which can return outputBufferDacTime with a different epoch | |
355 | output.device_frames = (unsigned)((time_info->outputBufferDacTime - stream_time) * output.current_sample_rate); | |
356 | } else { | |
357 | output.device_frames = 0; | |
358 | } | |
359 | ||
360 | output.updated = gettime_ms(); | |
361 | output.frames_played_dmp = output.frames_played; | |
362 | ||
363 | do { | |
364 | frames = _output_frames(pa_frames_wanted); | |
365 | pa_frames_wanted -= frames; | |
366 | } while (pa_frames_wanted > 0 && frames != 0); | |
367 | ||
368 | if (pa_frames_wanted > 0) { | |
369 | LOG_DEBUG("pad with silence"); | |
370 | memset(optr, 0, pa_frames_wanted * BYTES_PER_FRAME); | |
371 | } | |
372 | ||
373 | if (output.state == OUTPUT_OFF) { | |
374 | LOG_INFO("output off"); | |
375 | ret = paComplete; | |
376 | } else if (pa.rate != output.current_sample_rate) { | |
377 | ret = paComplete; | |
378 | } else { | |
379 | ret = paContinue; | |
380 | } | |
381 | ||
382 | UNLOCK; | |
383 | ||
384 | return ret; | |
385 | } | |
386 | ||
387 | void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, | |
388 | unsigned idle) { | |
389 | PaError err; | |
390 | unsigned latency = 0; | |
391 | int osx_playnice = -1; | |
392 | ||
393 | char *l = next_param(params, ':'); | |
394 | char *p = next_param(NULL, ':'); | |
395 | ||
396 | if (l) latency = (unsigned)atoi(l); | |
397 | if (p) osx_playnice = atoi(p); | |
398 | ||
399 | loglevel = level; | |
400 | ||
401 | LOG_INFO("init output"); | |
402 | ||
403 | memset(&output, 0, sizeof(output)); | |
404 | ||
405 | output.latency = latency; | |
406 | output.osx_playnice = osx_playnice; | |
407 | output.format = 0; | |
408 | output.start_frames = 0; | |
409 | output.write_cb = &_write_frames; | |
410 | output.rate_delay = rate_delay; | |
411 | pa.stream = NULL; | |
412 | ||
413 | LOG_INFO("requested latency: %u", output.latency); | |
414 | ||
415 | if ((err = Pa_Initialize()) != paNoError) { | |
416 | LOG_WARN("error initialising port audio: %s", Pa_GetErrorText(err)); | |
417 | exit(0); | |
418 | } | |
419 | ||
420 | output_init_common(level, device, output_buf_size, rates, idle); | |
421 | ||
422 | LOCK; | |
423 | ||
424 | _pa_open(); | |
425 | ||
426 | UNLOCK; | |
427 | } | |
428 | ||
429 | void output_close_pa(void) { | |
430 | PaError err; | |
431 | ||
432 | LOG_INFO("close output"); | |
433 | ||
434 | LOCK; | |
435 | ||
436 | running = false; | |
437 | monitor_thread_running = false; | |
438 | ||
439 | if (pa.stream) { | |
440 | if ((err = Pa_AbortStream(pa.stream)) != paNoError) { | |
441 | LOG_WARN("error closing stream: %s", Pa_GetErrorText(err)); | |
442 | } | |
443 | } | |
444 | ||
445 | if ((err = Pa_Terminate()) != paNoError) { | |
446 | LOG_WARN("error closing port audio: %s", Pa_GetErrorText(err)); | |
447 | } | |
448 | ||
449 | UNLOCK; | |
450 | ||
451 | output_close_common(); | |
452 | } | |
453 | ||
454 | #endif // PORTAUDIO |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // Scale and pack functions | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #define MAX_SCALESAMPLE 0x7fffffffffffLL | |
25 | #define MIN_SCALESAMPLE -MAX_SCALESAMPLE | |
26 | ||
27 | // inlining these on windows prevents them being linkable... | |
28 | #if !WIN | |
29 | inline | |
30 | #endif | |
31 | s32_t gain(s32_t gain, s32_t sample) { | |
32 | s64_t res = (s64_t)gain * (s64_t)sample; | |
33 | if (res > MAX_SCALESAMPLE) res = MAX_SCALESAMPLE; | |
34 | if (res < MIN_SCALESAMPLE) res = MIN_SCALESAMPLE; | |
35 | return (s32_t) (res >> 16); | |
36 | } | |
37 | #if !WIN | |
38 | inline | |
39 | #endif | |
40 | s32_t to_gain(float f) { | |
41 | return (s32_t)(f * 65536.0F); | |
42 | } | |
43 | ||
44 | void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, output_format format) { | |
45 | switch(format) { | |
46 | case S16_LE: | |
47 | { | |
48 | u32_t *optr = (u32_t *)(void *)outputptr; | |
49 | #if SL_LITTLE_ENDIAN | |
50 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
51 | while (cnt--) { | |
52 | *(optr++) = (*(inputptr) >> 16 & 0x0000ffff) | (*(inputptr + 1) & 0xffff0000); | |
53 | inputptr += 2; | |
54 | } | |
55 | } else { | |
56 | while (cnt--) { | |
57 | *(optr++) = (gain(gainL, *(inputptr)) >> 16 & 0x0000ffff) | (gain(gainR, *(inputptr+1)) & 0xffff0000); | |
58 | inputptr += 2; | |
59 | } | |
60 | } | |
61 | #else | |
62 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
63 | while (cnt--) { | |
64 | s32_t lsample = *(inputptr++); | |
65 | s32_t rsample = *(inputptr++); | |
66 | *(optr++) = | |
67 | (lsample & 0x00ff0000) << 8 | (lsample & 0xff000000) >> 8 | | |
68 | (rsample & 0x00ff0000) >> 8 | (rsample & 0xff000000) >> 24; | |
69 | } | |
70 | } else { | |
71 | while (cnt--) { | |
72 | s32_t lsample = gain(gainL, *(inputptr++)); | |
73 | s32_t rsample = gain(gainR, *(inputptr++)); | |
74 | *(optr++) = | |
75 | (lsample & 0x00ff0000) << 8 | (lsample & 0xff000000) >> 8 | | |
76 | (rsample & 0x00ff0000) >> 8 | (rsample & 0xff000000) >> 24; | |
77 | } | |
78 | } | |
79 | #endif | |
80 | } | |
81 | break; | |
82 | case S24_LE: | |
83 | { | |
84 | u32_t *optr = (u32_t *)(void *)outputptr; | |
85 | #if SL_LITTLE_ENDIAN | |
86 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
87 | while (cnt--) { | |
88 | *(optr++) = *(inputptr++) >> 8; | |
89 | *(optr++) = *(inputptr++) >> 8; | |
90 | } | |
91 | } else { | |
92 | while (cnt--) { | |
93 | *(optr++) = gain(gainL, *(inputptr++)) >> 8; | |
94 | *(optr++) = gain(gainR, *(inputptr++)) >> 8; | |
95 | } | |
96 | } | |
97 | #else | |
98 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
99 | while (cnt--) { | |
100 | s32_t lsample = *(inputptr++); | |
101 | s32_t rsample = *(inputptr++); | |
102 | *(optr++) = | |
103 | (lsample & 0xff000000) >> 16 | (lsample & 0x00ff0000) | (lsample & 0x0000ff00 << 16); | |
104 | *(optr++) = | |
105 | (rsample & 0xff000000) >> 16 | (rsample & 0x00ff0000) | (rsample & 0x0000ff00 << 16); | |
106 | } | |
107 | } else { | |
108 | while (cnt--) { | |
109 | s32_t lsample = gain(gainL, *(inputptr++)); | |
110 | s32_t rsample = gain(gainR, *(inputptr++)); | |
111 | *(optr++) = | |
112 | (lsample & 0xff000000) >> 16 | (lsample & 0x00ff0000) | (lsample & 0x0000ff00 << 16); | |
113 | *(optr++) = | |
114 | (rsample & 0xff000000) >> 16 | (rsample & 0x00ff0000) | (rsample & 0x0000ff00 << 16); | |
115 | } | |
116 | } | |
117 | #endif | |
118 | } | |
119 | break; | |
120 | case S24_3LE: | |
121 | { | |
122 | u8_t *optr = (u8_t *)(void *)outputptr; | |
123 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
124 | while (cnt) { | |
125 | // attempt to do 32 bit memory accesses - move 2 frames at once: 16 bytes -> 12 bytes | |
126 | // falls through to exception case when not aligned or if less than 2 frames to move | |
127 | if (((uintptr_t)optr & 0x3) == 0 && cnt >= 2) { | |
128 | u32_t *o_ptr = (u32_t *)(void *)optr; | |
129 | while (cnt >= 2) { | |
130 | s32_t l1 = *(inputptr++); s32_t r1 = *(inputptr++); | |
131 | s32_t l2 = *(inputptr++); s32_t r2 = *(inputptr++); | |
132 | #if SL_LITTLE_ENDIAN | |
133 | *(o_ptr++) = (l1 & 0xffffff00) >> 8 | (r1 & 0x0000ff00) << 16; | |
134 | *(o_ptr++) = (r1 & 0xffff0000) >> 16 | (l2 & 0x00ffff00) << 8; | |
135 | *(o_ptr++) = (l2 & 0xff000000) >> 24 | (r2 & 0xffffff00); | |
136 | #else | |
137 | *(o_ptr++) = (l1 & 0x0000ff00) << 16 | (l1 & 0x00ff0000) | (l1 & 0xff000000) >> 16 | | |
138 | (r1 & 0x0000ff00) >> 8; | |
139 | *(o_ptr++) = (r1 & 0x00ff0000) << 8 | (r1 & 0xff000000) >> 8 | (l2 & 0x0000ff00) | | |
140 | (l2 & 0x00ff0000) >> 16; | |
141 | *(o_ptr++) = (l2 & 0xff000000) | (r2 & 0x0000ff00) << 8 | (r2 & 0x00ff0000) >> 8 | | |
142 | (r2 & 0xff000000) >> 24; | |
143 | #endif | |
144 | optr += 12; | |
145 | cnt -= 2; | |
146 | } | |
147 | } else { | |
148 | s32_t lsample = *(inputptr++); | |
149 | s32_t rsample = *(inputptr++); | |
150 | *(optr++) = (lsample & 0x0000ff00) >> 8; | |
151 | *(optr++) = (lsample & 0x00ff0000) >> 16; | |
152 | *(optr++) = (lsample & 0xff000000) >> 24; | |
153 | *(optr++) = (rsample & 0x0000ff00) >> 8; | |
154 | *(optr++) = (rsample & 0x00ff0000) >> 16; | |
155 | *(optr++) = (rsample & 0xff000000) >> 24; | |
156 | cnt--; | |
157 | } | |
158 | } | |
159 | } else { | |
160 | while (cnt) { | |
161 | // attempt to do 32 bit memory accesses - move 2 frames at once: 16 bytes -> 12 bytes | |
162 | // falls through to exception case when not aligned or if less than 2 frames to move | |
163 | if (((uintptr_t)optr & 0x3) == 0 && cnt >= 2) { | |
164 | u32_t *o_ptr = (u32_t *)(void *)optr; | |
165 | while (cnt >= 2) { | |
166 | s32_t l1 = gain(gainL, *(inputptr++)); s32_t r1 = gain(gainR, *(inputptr++)); | |
167 | s32_t l2 = gain(gainL, *(inputptr++)); s32_t r2 = gain(gainR, *(inputptr++)); | |
168 | #if SL_LITTLE_ENDIAN | |
169 | *(o_ptr++) = (l1 & 0xffffff00) >> 8 | (r1 & 0x0000ff00) << 16; | |
170 | *(o_ptr++) = (r1 & 0xffff0000) >> 16 | (l2 & 0x00ffff00) << 8; | |
171 | *(o_ptr++) = (l2 & 0xff000000) >> 24 | (r2 & 0xffffff00); | |
172 | #else | |
173 | *(o_ptr++) = (l1 & 0x0000ff00) << 16 | (l1 & 0x00ff0000) | (l1 & 0xff000000) >> 16 | | |
174 | (r1 & 0x0000ff00) >> 8; | |
175 | *(o_ptr++) = (r1 & 0x00ff0000) << 8 | (r1 & 0xff000000) >> 8 | (l2 & 0x0000ff00) | | |
176 | (l2 & 0x00ff0000) >> 16; | |
177 | *(o_ptr++) = (l2 & 0xff000000) | (r2 & 0x0000ff00) << 8 | (r2 & 0x00ff0000) >> 8 | | |
178 | (r2 & 0xff000000) >> 24; | |
179 | #endif | |
180 | optr += 12; | |
181 | cnt -= 2; | |
182 | } | |
183 | } else { | |
184 | s32_t lsample = gain(gainL, *(inputptr++)); | |
185 | s32_t rsample = gain(gainR, *(inputptr++)); | |
186 | *(optr++) = (lsample & 0x0000ff00) >> 8; | |
187 | *(optr++) = (lsample & 0x00ff0000) >> 16; | |
188 | *(optr++) = (lsample & 0xff000000) >> 24; | |
189 | *(optr++) = (rsample & 0x0000ff00) >> 8; | |
190 | *(optr++) = (rsample & 0x00ff0000) >> 16; | |
191 | *(optr++) = (rsample & 0xff000000) >> 24; | |
192 | cnt--; | |
193 | } | |
194 | } | |
195 | } | |
196 | } | |
197 | break; | |
198 | case S32_LE: | |
199 | { | |
200 | u32_t *optr = (u32_t *)(void *)outputptr; | |
201 | #if SL_LITTLE_ENDIAN | |
202 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
203 | memcpy(outputptr, inputptr, cnt * BYTES_PER_FRAME); | |
204 | } else { | |
205 | while (cnt--) { | |
206 | *(optr++) = gain(gainL, *(inputptr++)); | |
207 | *(optr++) = gain(gainR, *(inputptr++)); | |
208 | } | |
209 | } | |
210 | #else | |
211 | if (gainL == FIXED_ONE && gainR == FIXED_ONE) { | |
212 | while (cnt--) { | |
213 | s32_t lsample = *(inputptr++); | |
214 | s32_t rsample = *(inputptr++); | |
215 | *(optr++) = | |
216 | (lsample & 0xff000000) >> 24 | (lsample & 0x00ff0000) >> 8 | | |
217 | (lsample & 0x0000ff00) << 8 | (lsample & 0x000000ff) << 24; | |
218 | *(optr++) = | |
219 | (rsample & 0xff000000) >> 24 | (rsample & 0x00ff0000) >> 8 | | |
220 | (rsample & 0x0000ff00) << 8 | (rsample & 0x000000ff) << 24; | |
221 | } | |
222 | } else { | |
223 | while (cnt--) { | |
224 | s32_t lsample = gain(gainL, *(inputptr++)); | |
225 | s32_t rsample = gain(gainR, *(inputptr++)); | |
226 | *(optr++) = | |
227 | (lsample & 0xff000000) >> 24 | (lsample & 0x00ff0000) >> 8 | | |
228 | (lsample & 0x0000ff00) << 8 | (lsample & 0x000000ff) << 24; | |
229 | *(optr++) = | |
230 | (rsample & 0xff000000) >> 24 | (rsample & 0x00ff0000) >> 8 | | |
231 | (rsample & 0x0000ff00) << 8 | (rsample & 0x000000ff) << 24; | |
232 | } | |
233 | } | |
234 | #endif | |
235 | } | |
236 | break; | |
237 | default: | |
238 | break; | |
239 | } | |
240 | } | |
241 | ||
242 | #if !WIN | |
243 | inline | |
244 | #endif | |
245 | void _apply_cross(struct buffer *outputbuf, frames_t out_frames, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) { | |
246 | s32_t *ptr = (s32_t *)(void *)outputbuf->readp; | |
247 | frames_t count = out_frames * 2; | |
248 | while (count--) { | |
249 | if (*cross_ptr > (s32_t *)outputbuf->wrap) { | |
250 | *cross_ptr -= outputbuf->size / BYTES_PER_FRAME * 2; | |
251 | } | |
252 | *ptr = gain(cross_gain_out, *ptr) + gain(cross_gain_in, **cross_ptr); | |
253 | ptr++; (*cross_ptr)++; | |
254 | } | |
255 | } | |
256 | ||
257 | #if !WIN | |
258 | inline | |
259 | #endif | |
260 | void _apply_gain(struct buffer *outputbuf, frames_t count, s32_t gainL, s32_t gainR) { | |
261 | s32_t *ptrL = (s32_t *)(void *)outputbuf->readp; | |
262 | s32_t *ptrR = (s32_t *)(void *)outputbuf->readp + 1; | |
263 | while (count--) { | |
264 | *ptrL = gain(gainL, *ptrL); | |
265 | *ptrR = gain(gainR, *ptrR); | |
266 | ptrL += 2; | |
267 | ptrR += 2; | |
268 | } | |
269 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // Stdout output | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #define FRAME_BLOCK MAX_SILENCE_FRAMES | |
25 | ||
26 | static log_level loglevel; | |
27 | ||
28 | static bool running = true; | |
29 | ||
30 | extern struct outputstate output; | |
31 | extern struct buffer *outputbuf; | |
32 | ||
33 | #define LOCK mutex_lock(outputbuf->mutex) | |
34 | #define UNLOCK mutex_unlock(outputbuf->mutex) | |
35 | ||
36 | extern u8_t *silencebuf; | |
37 | #if DSD | |
38 | extern u8_t *silencebuf_dop; | |
39 | #endif | |
40 | ||
41 | // buffer to hold output data so we can block on writing outside of output lock, allocated on init | |
42 | static u8_t *buf; | |
43 | static unsigned buffill; | |
44 | static int bytes_per_frame; | |
45 | ||
46 | static int _stdout_write_frames(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, | |
47 | s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr) { | |
48 | ||
49 | u8_t *obuf; | |
50 | ||
51 | if (!silence) { | |
52 | ||
53 | if (output.fade == FADE_ACTIVE && output.fade_dir == FADE_CROSS && *cross_ptr) { | |
54 | _apply_cross(outputbuf, out_frames, cross_gain_in, cross_gain_out, cross_ptr); | |
55 | } | |
56 | ||
57 | obuf = outputbuf->readp; | |
58 | ||
59 | } else { | |
60 | ||
61 | obuf = silencebuf; | |
62 | } | |
63 | ||
64 | IF_DSD( | |
65 | if (output.dop) { | |
66 | if (silence) { | |
67 | obuf = silencebuf_dop; | |
68 | } | |
69 | update_dop((u32_t *)obuf, out_frames, output.invert && !silence); | |
70 | } | |
71 | ) | |
72 | ||
73 | _scale_and_pack_frames(buf + buffill * bytes_per_frame, (s32_t *)(void *)obuf, out_frames, gainL, gainR, output.format); | |
74 | ||
75 | buffill += out_frames; | |
76 | ||
77 | return (int)out_frames; | |
78 | } | |
79 | ||
80 | static void *output_thread() { | |
81 | ||
82 | LOCK; | |
83 | ||
84 | switch (output.format) { | |
85 | case S32_LE: | |
86 | bytes_per_frame = 4 * 2; break; | |
87 | case S24_3LE: | |
88 | bytes_per_frame = 3 * 2; break; | |
89 | case S16_LE: | |
90 | bytes_per_frame = 2 * 2; break; | |
91 | default: | |
92 | bytes_per_frame = 4 * 2; break; | |
93 | break; | |
94 | } | |
95 | ||
96 | UNLOCK; | |
97 | ||
98 | while (running) { | |
99 | ||
100 | LOCK; | |
101 | ||
102 | output.device_frames = 0; | |
103 | output.updated = gettime_ms(); | |
104 | output.frames_played_dmp = output.frames_played; | |
105 | ||
106 | _output_frames(FRAME_BLOCK); | |
107 | ||
108 | UNLOCK; | |
109 | ||
110 | if (buffill) { | |
111 | fwrite(buf, bytes_per_frame, buffill, stdout); | |
112 | buffill = 0; | |
113 | } | |
114 | ||
115 | } | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
120 | static thread_type thread; | |
121 | ||
122 | void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay) { | |
123 | loglevel = level; | |
124 | ||
125 | LOG_INFO("init output stdout"); | |
126 | ||
127 | buf = malloc(FRAME_BLOCK * BYTES_PER_FRAME); | |
128 | if (!buf) { | |
129 | LOG_ERROR("unable to malloc buf"); | |
130 | return; | |
131 | } | |
132 | buffill = 0; | |
133 | ||
134 | memset(&output, 0, sizeof(output)); | |
135 | ||
136 | output.format = S32_LE; | |
137 | output.start_frames = FRAME_BLOCK * 2; | |
138 | output.write_cb = &_stdout_write_frames; | |
139 | output.rate_delay = rate_delay; | |
140 | ||
141 | if (params) { | |
142 | if (!strcmp(params, "32")) output.format = S32_LE; | |
143 | if (!strcmp(params, "24")) output.format = S24_3LE; | |
144 | if (!strcmp(params, "16")) output.format = S16_LE; | |
145 | } | |
146 | ||
147 | // ensure output rate is specified to avoid test open | |
148 | if (!rates[0]) { | |
149 | rates[0] = 44100; | |
150 | } | |
151 | ||
152 | output_init_common(level, "-", output_buf_size, rates, 0); | |
153 | ||
154 | #if LINUX || OSX || FREEBSD | |
155 | pthread_attr_t attr; | |
156 | pthread_attr_init(&attr); | |
157 | pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + OUTPUT_THREAD_STACK_SIZE); | |
158 | pthread_create(&thread, &attr, output_thread, NULL); | |
159 | pthread_attr_destroy(&attr); | |
160 | #endif | |
161 | #if WIN | |
162 | thread = CreateThread(NULL, OUTPUT_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&output_thread, NULL, 0, NULL); | |
163 | #endif | |
164 | } | |
165 | ||
166 | void output_close_stdout(void) { | |
167 | LOG_INFO("close output"); | |
168 | ||
169 | LOCK; | |
170 | running = false; | |
171 | UNLOCK; | |
172 | ||
173 | free(buf); | |
174 | ||
175 | output_close_common(); | |
176 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // Export audio samples for visualiser process (16 bit only best endevours) | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #if VISEXPORT | |
25 | ||
26 | #include <sys/stat.h> | |
27 | #include <sys/mman.h> | |
28 | #include <fcntl.h> | |
29 | ||
30 | #define VIS_BUF_SIZE 16384 | |
31 | #define VIS_LOCK_NS 1000000 // ns to wait for vis wrlock | |
32 | ||
33 | static struct vis_t { | |
34 | pthread_rwlock_t rwlock; | |
35 | u32_t buf_size; | |
36 | u32_t buf_index; | |
37 | bool running; | |
38 | u32_t rate; | |
39 | time_t updated; | |
40 | s16_t buffer[VIS_BUF_SIZE]; | |
41 | } *vis_mmap = NULL; | |
42 | ||
43 | static char vis_shm_path[40]; | |
44 | static int vis_fd = -1; | |
45 | ||
46 | static log_level loglevel; | |
47 | ||
48 | // attempt to write audio to vis_mmap but do not wait more than VIS_LOCK_NS to get wrlock | |
49 | // this can result in missing audio export to the mmap region, but this is preferable dropping audio | |
50 | void _vis_export(struct buffer *outputbuf, struct outputstate *output, frames_t out_frames, bool silence) { | |
51 | if (vis_mmap) { | |
52 | int err; | |
53 | ||
54 | err = pthread_rwlock_trywrlock(&vis_mmap->rwlock); | |
55 | if (err) { | |
56 | struct timespec ts; | |
57 | clock_gettime(CLOCK_REALTIME, &ts); | |
58 | ts.tv_nsec += VIS_LOCK_NS; | |
59 | if (ts.tv_nsec > 1000000000) { | |
60 | ts.tv_sec += 1; | |
61 | ts.tv_nsec -= 1000000000; | |
62 | } | |
63 | err = pthread_rwlock_timedwrlock(&vis_mmap->rwlock, &ts); | |
64 | } | |
65 | ||
66 | if (err) { | |
67 | LOG_DEBUG("failed to get wrlock - skipping visulizer export"); | |
68 | ||
69 | } else { | |
70 | ||
71 | if (silence) { | |
72 | vis_mmap->running = false; | |
73 | } else { | |
74 | frames_t vis_cnt = out_frames; | |
75 | s32_t *ptr = (s32_t *) outputbuf->readp; | |
76 | unsigned i = vis_mmap->buf_index; | |
77 | ||
78 | if (!output->current_replay_gain) { | |
79 | while (vis_cnt--) { | |
80 | vis_mmap->buffer[i++] = *(ptr++) >> 16; | |
81 | vis_mmap->buffer[i++] = *(ptr++) >> 16; | |
82 | if (i == VIS_BUF_SIZE) i = 0; | |
83 | } | |
84 | } else { | |
85 | while (vis_cnt--) { | |
86 | vis_mmap->buffer[i++] = gain(*(ptr++), output->current_replay_gain) >> 16; | |
87 | vis_mmap->buffer[i++] = gain(*(ptr++), output->current_replay_gain) >> 16; | |
88 | if (i == VIS_BUF_SIZE) i = 0; | |
89 | } | |
90 | } | |
91 | ||
92 | vis_mmap->updated = time(NULL); | |
93 | vis_mmap->running = true; | |
94 | vis_mmap->buf_index = i; | |
95 | vis_mmap->rate = output->current_sample_rate; | |
96 | } | |
97 | ||
98 | pthread_rwlock_unlock(&vis_mmap->rwlock); | |
99 | } | |
100 | } | |
101 | } | |
102 | ||
103 | void vis_stop(void) { | |
104 | if (vis_mmap) { | |
105 | pthread_rwlock_wrlock(&vis_mmap->rwlock); | |
106 | vis_mmap->running = false; | |
107 | pthread_rwlock_unlock(&vis_mmap->rwlock); | |
108 | } | |
109 | } | |
110 | ||
111 | void output_vis_init(log_level level, u8_t *mac) { | |
112 | loglevel = level; | |
113 | ||
114 | sprintf(vis_shm_path, "/squeezelite-%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); | |
115 | ||
116 | mode_t old_mask = umask(000); // allow any user to read our shm when created | |
117 | ||
118 | vis_fd = shm_open(vis_shm_path, O_CREAT | O_RDWR, 0666); | |
119 | if (vis_fd != -1) { | |
120 | if (ftruncate(vis_fd, sizeof(struct vis_t)) == 0) { | |
121 | vis_mmap = (struct vis_t *)mmap(NULL, sizeof(struct vis_t), PROT_READ | PROT_WRITE, MAP_SHARED, vis_fd, 0); | |
122 | } | |
123 | } | |
124 | ||
125 | if (vis_mmap > 0) { | |
126 | pthread_rwlockattr_t attr; | |
127 | pthread_rwlockattr_init(&attr); | |
128 | pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); | |
129 | pthread_rwlock_init(&vis_mmap->rwlock, &attr); | |
130 | vis_mmap->buf_size = VIS_BUF_SIZE; | |
131 | vis_mmap->running = false; | |
132 | vis_mmap->rate = 44100; | |
133 | pthread_rwlockattr_destroy(&attr); | |
134 | LOG_INFO("opened visulizer shared memory as %s", vis_shm_path); | |
135 | } else { | |
136 | LOG_WARN("unable to open visualizer shared memory"); | |
137 | vis_mmap = NULL; | |
138 | } | |
139 | ||
140 | umask(old_mask); | |
141 | } | |
142 | ||
143 | #endif // VISEXPORT |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 "squeezelite.h" | |
21 | ||
22 | extern log_level loglevel; | |
23 | ||
24 | extern struct buffer *streambuf; | |
25 | extern struct buffer *outputbuf; | |
26 | extern struct streamstate stream; | |
27 | extern struct outputstate output; | |
28 | extern struct decodestate decode; | |
29 | extern struct processstate process; | |
30 | ||
31 | #define LOCK_S mutex_lock(streambuf->mutex) | |
32 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
33 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
34 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
35 | #if PROCESS | |
36 | #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex) | |
37 | #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex) | |
38 | #define LOCK_O_not_direct if (!decode.direct) mutex_lock(outputbuf->mutex) | |
39 | #define UNLOCK_O_not_direct if (!decode.direct) mutex_unlock(outputbuf->mutex) | |
40 | #define IF_DIRECT(x) if (decode.direct) { x } | |
41 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
42 | #else | |
43 | #define LOCK_O_direct mutex_lock(outputbuf->mutex) | |
44 | #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex) | |
45 | #define LOCK_O_not_direct | |
46 | #define UNLOCK_O_not_direct | |
47 | #define IF_DIRECT(x) { x } | |
48 | #define IF_PROCESS(x) | |
49 | #endif | |
50 | ||
51 | #define MAX_DECODE_FRAMES 4096 | |
52 | ||
53 | static u32_t sample_rates[] = { | |
54 | 11025, 22050, 32000, 44100, 48000, 8000, 12000, 16000, 24000, 96000, 88200, 176400, 192000, 352800, 384000 | |
55 | }; | |
56 | ||
57 | static u32_t sample_rate; | |
58 | static u32_t sample_size; | |
59 | static u32_t channels; | |
60 | static bool bigendian; | |
61 | static bool limit; | |
62 | static u32_t audio_left; | |
63 | static u32_t bytes_per_frame; | |
64 | ||
65 | typedef enum { UNKNOWN = 0, WAVE, AIFF } header_format; | |
66 | ||
67 | static void _check_header(void) { | |
68 | u8_t *ptr = streambuf->readp; | |
69 | unsigned bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
70 | header_format format = UNKNOWN; | |
71 | ||
72 | // simple parsing of wav and aiff headers and get to samples | |
73 | ||
74 | if (bytes > 12) { | |
75 | if (!memcmp(ptr, "RIFF", 4) && !memcmp(ptr+8, "WAVE", 4)) { | |
76 | LOG_INFO("WAVE"); | |
77 | format = WAVE; | |
78 | } else if (!memcmp(ptr, "FORM", 4) && (!memcmp(ptr+8, "AIFF", 4) || !memcmp(ptr+8, "AIFC", 4))) { | |
79 | LOG_INFO("AIFF"); | |
80 | format = AIFF; | |
81 | } | |
82 | } | |
83 | ||
84 | if (format != UNKNOWN) { | |
85 | ptr += 12; | |
86 | bytes -= 12; | |
87 | ||
88 | while (bytes >= 8) { | |
89 | char id[5]; | |
90 | unsigned len; | |
91 | memcpy(id, ptr, 4); | |
92 | id[4] = '\0'; | |
93 | ||
94 | if (format == WAVE) { | |
95 | len = *(ptr+4) | *(ptr+5) << 8 | *(ptr+6) << 16| *(ptr+7) << 24; | |
96 | } else { | |
97 | len = *(ptr+4) << 24 | *(ptr+5) << 16 | *(ptr+6) << 8 | *(ptr+7); | |
98 | } | |
99 | ||
100 | LOG_INFO("header: %s len: %d", id, len); | |
101 | ||
102 | if (format == WAVE && !memcmp(ptr, "data", 4)) { | |
103 | ptr += 8; | |
104 | _buf_inc_readp(streambuf, ptr - streambuf->readp); | |
105 | audio_left = len; | |
106 | LOG_INFO("audio size: %u", audio_left); | |
107 | limit = true; | |
108 | return; | |
109 | } | |
110 | ||
111 | if (format == AIFF && !memcmp(ptr, "SSND", 4) && bytes >= 16) { | |
112 | unsigned offset = *(ptr+8) << 24 | *(ptr+9) << 16 | *(ptr+10) << 8 | *(ptr+11); | |
113 | // following 4 bytes is blocksize - ignored | |
114 | ptr += 8 + 8; | |
115 | _buf_inc_readp(streambuf, ptr + offset - streambuf->readp); | |
116 | audio_left = len - 8 - offset; | |
117 | LOG_INFO("audio size: %u", audio_left); | |
118 | limit = true; | |
119 | return; | |
120 | } | |
121 | ||
122 | if (format == WAVE && !memcmp(ptr, "fmt ", 4) && bytes >= 24) { | |
123 | // override the server parsed values with our own | |
124 | channels = *(ptr+10) | *(ptr+11) << 8; | |
125 | sample_rate = *(ptr+12) | *(ptr+13) << 8 | *(ptr+14) << 16 | *(ptr+15) << 24; | |
126 | sample_size = (*(ptr+22) | *(ptr+23) << 8) / 8; | |
127 | bigendian = 0; | |
128 | LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian); | |
129 | } | |
130 | ||
131 | if (format == AIFF && !memcmp(ptr, "COMM", 4) && bytes >= 26) { | |
132 | int exponent; | |
133 | // override the server parsed values with our own | |
134 | channels = *(ptr+8) << 8 | *(ptr+9); | |
135 | sample_size = (*(ptr+14) << 8 | *(ptr+15)) / 8; | |
136 | bigendian = 1; | |
137 | // sample rate is encoded as IEEE 80 bit extended format | |
138 | // make some assumptions to simplify processing - only use first 32 bits of mantissa | |
139 | exponent = ((*(ptr+16) & 0x7f) << 8 | *(ptr+17)) - 16383 - 31; | |
140 | sample_rate = *(ptr+18) << 24 | *(ptr+19) << 16 | *(ptr+20) << 8 | *(ptr+21); | |
141 | while (exponent < 0) { sample_rate >>= 1; ++exponent; } | |
142 | while (exponent > 0) { sample_rate <<= 1; --exponent; } | |
143 | LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian); | |
144 | } | |
145 | ||
146 | if (bytes >= len + 8) { | |
147 | ptr += len + 8; | |
148 | bytes -= (len + 8); | |
149 | } else { | |
150 | LOG_WARN("run out of data"); | |
151 | return; | |
152 | } | |
153 | } | |
154 | ||
155 | } else { | |
156 | LOG_WARN("unknown format - can't parse header"); | |
157 | } | |
158 | } | |
159 | ||
160 | static decode_state pcm_decode(void) { | |
161 | unsigned bytes, in, out; | |
162 | frames_t frames, count; | |
163 | u32_t *optr; | |
164 | u8_t *iptr; | |
165 | u8_t tmp[16]; | |
166 | ||
167 | LOCK_S; | |
168 | ||
169 | if (decode.new_stream && stream.state == STREAMING_FILE) { | |
170 | _check_header(); | |
171 | } | |
172 | ||
173 | LOCK_O_direct; | |
174 | ||
175 | bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
176 | ||
177 | IF_DIRECT( | |
178 | out = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; | |
179 | ); | |
180 | IF_PROCESS( | |
181 | out = process.max_in_frames; | |
182 | ); | |
183 | ||
184 | if ((stream.state <= DISCONNECT && bytes == 0) || (limit && audio_left == 0)) { | |
185 | UNLOCK_O_direct; | |
186 | UNLOCK_S; | |
187 | return DECODE_COMPLETE; | |
188 | } | |
189 | ||
190 | if (decode.new_stream) { | |
191 | LOG_INFO("setting track_start"); | |
192 | LOCK_O_not_direct; | |
193 | output.next_sample_rate = decode_newstream(sample_rate, output.supported_rates); | |
194 | output.track_start = outputbuf->writep; | |
195 | IF_DSD( output.next_dop = false; ) | |
196 | if (output.fade_mode) _checkfade(true); | |
197 | decode.new_stream = false; | |
198 | UNLOCK_O_not_direct; | |
199 | IF_PROCESS( | |
200 | out = process.max_in_frames; | |
201 | ); | |
202 | bytes_per_frame = channels * sample_size; | |
203 | } | |
204 | ||
205 | IF_DIRECT( | |
206 | optr = (u32_t *)outputbuf->writep; | |
207 | ); | |
208 | IF_PROCESS( | |
209 | optr = (u32_t *)process.inbuf; | |
210 | ); | |
211 | iptr = (u8_t *)streambuf->readp; | |
212 | ||
213 | in = bytes / bytes_per_frame; | |
214 | ||
215 | // handle frame wrapping round end of streambuf | |
216 | // - only need if resizing of streambuf does not avoid this, could occur in localfile case | |
217 | if (in == 0 && bytes > 0 && _buf_used(streambuf) >= bytes_per_frame) { | |
218 | memcpy(tmp, iptr, bytes); | |
219 | memcpy(tmp + bytes, streambuf->buf, bytes_per_frame - bytes); | |
220 | iptr = tmp; | |
221 | in = 1; | |
222 | } | |
223 | ||
224 | frames = min(in, out); | |
225 | frames = min(frames, MAX_DECODE_FRAMES); | |
226 | ||
227 | if (limit && frames * bytes_per_frame > audio_left) { | |
228 | LOG_INFO("reached end of audio"); | |
229 | frames = audio_left / bytes_per_frame; | |
230 | } | |
231 | ||
232 | count = frames * channels; | |
233 | ||
234 | if (channels == 2) { | |
235 | if (sample_size == 1) { | |
236 | while (count--) { | |
237 | *optr++ = *iptr++ << 24; | |
238 | } | |
239 | } else if (sample_size == 2) { | |
240 | if (bigendian) { | |
241 | while (count--) { | |
242 | *optr++ = *(iptr) << 24 | *(iptr+1) << 16; | |
243 | iptr += 2; | |
244 | } | |
245 | } else { | |
246 | while (count--) { | |
247 | *optr++ = *(iptr) << 16 | *(iptr+1) << 24; | |
248 | iptr += 2; | |
249 | } | |
250 | } | |
251 | } else if (sample_size == 3) { | |
252 | if (bigendian) { | |
253 | while (count--) { | |
254 | *optr++ = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8; | |
255 | iptr += 3; | |
256 | } | |
257 | } else { | |
258 | while (count--) { | |
259 | *optr++ = *(iptr) << 8 | *(iptr+1) << 16 | *(iptr+2) << 24; | |
260 | iptr += 3; | |
261 | } | |
262 | } | |
263 | } else if (sample_size == 4) { | |
264 | if (bigendian) { | |
265 | while (count--) { | |
266 | *optr++ = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3); | |
267 | iptr += 4; | |
268 | } | |
269 | } else { | |
270 | while (count--) { | |
271 | *optr++ = *(iptr) | *(iptr+1) << 8 | *(iptr+2) << 16 | *(iptr+3) << 24; | |
272 | iptr += 4; | |
273 | } | |
274 | } | |
275 | } | |
276 | } else if (channels == 1) { | |
277 | if (sample_size == 1) { | |
278 | while (count--) { | |
279 | *optr = *iptr++ << 24; | |
280 | *(optr+1) = *optr; | |
281 | optr += 2; | |
282 | } | |
283 | } else if (sample_size == 2) { | |
284 | if (bigendian) { | |
285 | while (count--) { | |
286 | *optr = *(iptr) << 24 | *(iptr+1) << 16; | |
287 | *(optr+1) = *optr; | |
288 | iptr += 2; | |
289 | optr += 2; | |
290 | } | |
291 | } else { | |
292 | while (count--) { | |
293 | *optr = *(iptr) << 16 | *(iptr+1) << 24; | |
294 | *(optr+1) = *optr; | |
295 | iptr += 2; | |
296 | optr += 2; | |
297 | } | |
298 | } | |
299 | } else if (sample_size == 3) { | |
300 | if (bigendian) { | |
301 | while (count--) { | |
302 | *optr = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8; | |
303 | *(optr+1) = *optr; | |
304 | iptr += 3; | |
305 | optr += 2; | |
306 | } | |
307 | } else { | |
308 | while (count--) { | |
309 | *optr = *(iptr) << 8 | *(iptr+1) << 16 | *(iptr+2) << 24; | |
310 | *(optr+1) = *optr; | |
311 | iptr += 3; | |
312 | optr += 2; | |
313 | } | |
314 | } | |
315 | } else if (sample_size == 4) { | |
316 | if (bigendian) { | |
317 | while (count--) { | |
318 | *optr++ = *(iptr) << 24 | *(iptr+1) << 16 | *(iptr+2) << 8 | *(iptr+3); | |
319 | *(optr+1) = *optr; | |
320 | iptr += 4; | |
321 | optr += 2; | |
322 | } | |
323 | } else { | |
324 | while (count--) { | |
325 | *optr++ = *(iptr) | *(iptr+1) << 8 | *(iptr+2) << 16 | *(iptr+3) << 24; | |
326 | *(optr+1) = *optr; | |
327 | iptr += 4; | |
328 | optr += 2; | |
329 | } | |
330 | } | |
331 | } | |
332 | } else { | |
333 | LOG_ERROR("unsupported channels"); | |
334 | } | |
335 | ||
336 | LOG_SDEBUG("decoded %u frames", frames); | |
337 | ||
338 | _buf_inc_readp(streambuf, frames * bytes_per_frame); | |
339 | ||
340 | if (limit) { | |
341 | audio_left -= frames * bytes_per_frame; | |
342 | } | |
343 | ||
344 | IF_DIRECT( | |
345 | _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); | |
346 | ); | |
347 | IF_PROCESS( | |
348 | process.in_frames = frames; | |
349 | ); | |
350 | ||
351 | UNLOCK_O_direct; | |
352 | UNLOCK_S; | |
353 | ||
354 | return DECODE_RUNNING; | |
355 | } | |
356 | ||
357 | static void pcm_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
358 | sample_size = size - '0' + 1; | |
359 | sample_rate = sample_rates[rate - '0']; | |
360 | channels = chan - '0'; | |
361 | bigendian = (endianness == '0'); | |
362 | limit = false; | |
363 | ||
364 | LOG_INFO("pcm size: %u rate: %u chan: %u bigendian: %u", sample_size, sample_rate, channels, bigendian); | |
365 | buf_adjust(streambuf, sample_size * channels); | |
366 | } | |
367 | ||
368 | static void pcm_close(void) { | |
369 | buf_adjust(streambuf, 1); | |
370 | } | |
371 | ||
372 | struct codec *register_pcm(void) { | |
373 | static struct codec ret = { | |
374 | 'p', // id | |
375 | "aif,pcm", // types | |
376 | 4096, // min read | |
377 | 102400, // min space | |
378 | pcm_open, // open | |
379 | pcm_close, // close | |
380 | pcm_decode, // decode | |
381 | }; | |
382 | ||
383 | LOG_INFO("using pcm to decode aif,pcm"); | |
384 | return &ret; | |
385 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // sample processing - only included when building with PROCESS set | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #if PROCESS | |
25 | ||
26 | extern log_level loglevel; | |
27 | ||
28 | extern struct buffer *outputbuf; | |
29 | extern struct decodestate decode; | |
30 | struct processstate process; | |
31 | extern struct codec *codec; | |
32 | ||
33 | #define LOCK_D mutex_lock(decode.mutex); | |
34 | #define UNLOCK_D mutex_unlock(decode.mutex); | |
35 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
36 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
37 | ||
38 | // macros to map to processing functions - currently only resample.c | |
39 | // this can be made more generic when multiple processing mechanisms get added | |
40 | #if RESAMPLE | |
41 | #define SAMPLES_FUNC resample_samples | |
42 | #define DRAIN_FUNC resample_drain | |
43 | #define NEWSTREAM_FUNC resample_newstream | |
44 | #define FLUSH_FUNC resample_flush | |
45 | #define INIT_FUNC resample_init | |
46 | #endif | |
47 | ||
48 | ||
49 | // transfer all processed frames to the output buf | |
50 | static void _write_samples(void) { | |
51 | size_t frames = process.out_frames; | |
52 | u32_t *iptr = (u32_t *)process.outbuf; | |
53 | unsigned cnt = 10; | |
54 | ||
55 | LOCK_O; | |
56 | ||
57 | while (frames > 0) { | |
58 | ||
59 | frames_t f = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; | |
60 | u32_t *optr = (u32_t *)outputbuf->writep; | |
61 | ||
62 | if (f > 0) { | |
63 | ||
64 | f = min(f, frames); | |
65 | ||
66 | memcpy(optr, iptr, f * BYTES_PER_FRAME); | |
67 | ||
68 | frames -= f; | |
69 | ||
70 | _buf_inc_writep(outputbuf, f * BYTES_PER_FRAME); | |
71 | iptr += f * BYTES_PER_FRAME / sizeof(*iptr); | |
72 | ||
73 | } else if (cnt--) { | |
74 | ||
75 | // there should normally be space in the output buffer, but may need to wait during drain phase | |
76 | UNLOCK_O; | |
77 | usleep(10000); | |
78 | LOCK_O; | |
79 | ||
80 | } else { | |
81 | ||
82 | // bail out if no space found after 100ms to avoid locking | |
83 | LOG_ERROR("unable to get space in output buffer"); | |
84 | UNLOCK_O; | |
85 | return; | |
86 | } | |
87 | } | |
88 | ||
89 | UNLOCK_O; | |
90 | } | |
91 | ||
92 | // process samples - called with decode mutex set | |
93 | void process_samples(void) { | |
94 | ||
95 | SAMPLES_FUNC(&process); | |
96 | ||
97 | _write_samples(); | |
98 | ||
99 | process.in_frames = 0; | |
100 | } | |
101 | ||
102 | // drain at end of track - called with decode mutex set | |
103 | void process_drain(void) { | |
104 | bool done; | |
105 | ||
106 | do { | |
107 | ||
108 | done = DRAIN_FUNC(&process); | |
109 | ||
110 | _write_samples(); | |
111 | ||
112 | } while (!done); | |
113 | ||
114 | LOG_DEBUG("processing track complete - frames in: %lu out: %lu", process.total_in, process.total_out); | |
115 | } | |
116 | ||
117 | // new stream - called with decode mutex set | |
118 | unsigned process_newstream(bool *direct, unsigned raw_sample_rate, unsigned supported_rates[]) { | |
119 | ||
120 | bool active = NEWSTREAM_FUNC(&process, raw_sample_rate, supported_rates); | |
121 | ||
122 | LOG_INFO("processing: %s", active ? "active" : "inactive"); | |
123 | ||
124 | *direct = !active; | |
125 | ||
126 | if (active) { | |
127 | ||
128 | unsigned max_in_frames, max_out_frames; | |
129 | ||
130 | process.in_frames = process.out_frames = 0; | |
131 | process.total_in = process.total_out = 0; | |
132 | ||
133 | max_in_frames = codec->min_space / BYTES_PER_FRAME ; | |
134 | ||
135 | // increase size of output buffer by 10% as output rate is not an exact multiple of input rate | |
136 | if (process.out_sample_rate % process.in_sample_rate == 0) { | |
137 | max_out_frames = max_in_frames * (process.out_sample_rate / process.in_sample_rate); | |
138 | } else { | |
139 | max_out_frames = (int)(1.1 * (float)max_in_frames * (float)process.out_sample_rate / (float)process.in_sample_rate); | |
140 | } | |
141 | ||
142 | if (process.max_in_frames != max_in_frames) { | |
143 | LOG_DEBUG("creating process buf in frames: %u", max_in_frames); | |
144 | if (process.inbuf) free(process.inbuf); | |
145 | process.inbuf = malloc(max_in_frames * BYTES_PER_FRAME); | |
146 | process.max_in_frames = max_in_frames; | |
147 | } | |
148 | ||
149 | if (process.max_out_frames != max_out_frames) { | |
150 | LOG_DEBUG("creating process buf out frames: %u", max_out_frames); | |
151 | if (process.outbuf) free(process.outbuf); | |
152 | process.outbuf = malloc(max_out_frames * BYTES_PER_FRAME); | |
153 | process.max_out_frames = max_out_frames; | |
154 | } | |
155 | ||
156 | if (!process.inbuf || !process.outbuf) { | |
157 | LOG_ERROR("malloc fail creating process buffers"); | |
158 | *direct = true; | |
159 | return raw_sample_rate; | |
160 | } | |
161 | ||
162 | return process.out_sample_rate; | |
163 | } | |
164 | ||
165 | return raw_sample_rate; | |
166 | } | |
167 | ||
168 | // process flush - called with decode mutex set | |
169 | void process_flush(void) { | |
170 | ||
171 | LOG_INFO("process flush"); | |
172 | ||
173 | FLUSH_FUNC(); | |
174 | ||
175 | process.in_frames = 0; | |
176 | } | |
177 | ||
178 | // init - called with no mutex | |
179 | void process_init(char *opt) { | |
180 | ||
181 | bool enabled = INIT_FUNC(opt); | |
182 | ||
183 | memset(&process, 0, sizeof(process)); | |
184 | ||
185 | if (enabled) { | |
186 | LOCK_D; | |
187 | decode.process = true; | |
188 | UNLOCK_D; | |
189 | } | |
190 | } | |
191 | ||
192 | #endif // #if PROCESS |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // upsampling using libsoxr - only included if RESAMPLE set | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #if RESAMPLE | |
25 | ||
26 | #include <math.h> | |
27 | #include <soxr.h> | |
28 | ||
29 | extern log_level loglevel; | |
30 | ||
31 | struct soxr { | |
32 | soxr_t resampler; | |
33 | size_t old_clips; | |
34 | unsigned long q_recipe; | |
35 | unsigned long q_flags; | |
36 | double q_precision; /* Conversion precision (in bits). 20 */ | |
37 | double q_phase_response; /* 0=minimum, ... 50=linear, ... 100=maximum 50 */ | |
38 | double q_passband_end; /* 0dB pt. bandwidth to preserve; nyquist=1 0.913 */ | |
39 | double q_stopband_begin; /* Aliasing/imaging control; > passband_end 1 */ | |
40 | double scale; | |
41 | bool max_rate; | |
42 | bool exception; | |
43 | #if !LINKALL | |
44 | // soxr symbols to be dynamically loaded | |
45 | soxr_io_spec_t (* soxr_io_spec)(soxr_datatype_t itype, soxr_datatype_t otype); | |
46 | soxr_quality_spec_t (* soxr_quality_spec)(unsigned long recipe, unsigned long flags); | |
47 | soxr_t (* soxr_create)(double, double, unsigned, soxr_error_t *, | |
48 | soxr_io_spec_t const *, soxr_quality_spec_t const *, soxr_runtime_spec_t const *); | |
49 | void (* soxr_delete)(soxr_t); | |
50 | soxr_error_t (* soxr_process)(soxr_t, soxr_in_t, size_t, size_t *, soxr_out_t, size_t olen, size_t *); | |
51 | size_t *(* soxr_num_clips)(soxr_t); | |
52 | #if RESAMPLE_MP | |
53 | soxr_runtime_spec_t (* soxr_runtime_spec)(unsigned num_threads); | |
54 | #endif | |
55 | // soxr_strerror is a macro so not included here | |
56 | #endif | |
57 | }; | |
58 | ||
59 | static struct soxr *r; | |
60 | ||
61 | #if LINKALL | |
62 | #define SOXR(h, fn, ...) (soxr_ ## fn)(__VA_ARGS__) | |
63 | #else | |
64 | #define SOXR(h, fn, ...) (h)->soxr_##fn(__VA_ARGS__) | |
65 | #endif | |
66 | ||
67 | ||
68 | void resample_samples(struct processstate *process) { | |
69 | size_t idone, odone; | |
70 | size_t clip_cnt; | |
71 | ||
72 | soxr_error_t error = | |
73 | SOXR(r, process, r->resampler, process->inbuf, process->in_frames, &idone, process->outbuf, process->max_out_frames, &odone); | |
74 | if (error) { | |
75 | LOG_INFO("soxr_process error: %s", soxr_strerror(error)); | |
76 | return; | |
77 | } | |
78 | ||
79 | if (idone != process->in_frames) { | |
80 | // should not get here if buffers are big enough... | |
81 | LOG_ERROR("should not get here - partial sox process: %u of %u processed %u of %u out", | |
82 | (unsigned)idone, process->in_frames, (unsigned)odone, process->max_out_frames); | |
83 | } | |
84 | ||
85 | process->out_frames = odone; | |
86 | process->total_in += idone; | |
87 | process->total_out += odone; | |
88 | ||
89 | clip_cnt = *(SOXR(r, num_clips, r->resampler)); | |
90 | if (clip_cnt - r->old_clips) { | |
91 | LOG_SDEBUG("resampling clips: %u", (unsigned)(clip_cnt - r->old_clips)); | |
92 | r->old_clips = clip_cnt; | |
93 | } | |
94 | } | |
95 | ||
96 | bool resample_drain(struct processstate *process) { | |
97 | size_t odone; | |
98 | size_t clip_cnt; | |
99 | ||
100 | soxr_error_t error = SOXR(r, process, r->resampler, NULL, 0, NULL, process->outbuf, process->max_out_frames, &odone); | |
101 | if (error) { | |
102 | LOG_INFO("soxr_process error: %s", soxr_strerror(error)); | |
103 | return true; | |
104 | } | |
105 | ||
106 | process->out_frames = odone; | |
107 | process->total_out += odone; | |
108 | ||
109 | clip_cnt = *(SOXR(r, num_clips, r->resampler)); | |
110 | if (clip_cnt - r->old_clips) { | |
111 | LOG_DEBUG("resampling clips: %u", (unsigned)(clip_cnt - r->old_clips)); | |
112 | r->old_clips = clip_cnt; | |
113 | } | |
114 | ||
115 | if (odone == 0) { | |
116 | ||
117 | LOG_INFO("resample track complete - total track clips: %u", r->old_clips); | |
118 | ||
119 | SOXR(r, delete, r->resampler); | |
120 | r->resampler = NULL; | |
121 | ||
122 | return true; | |
123 | ||
124 | } else { | |
125 | ||
126 | return false; | |
127 | } | |
128 | } | |
129 | ||
130 | bool resample_newstream(struct processstate *process, unsigned raw_sample_rate, unsigned supported_rates[]) { | |
131 | unsigned outrate = 0; | |
132 | int i; | |
133 | ||
134 | if (r->exception) { | |
135 | // find direct match - avoid resampling | |
136 | for (i = 0; supported_rates[i]; i++) { | |
137 | if (raw_sample_rate == supported_rates[i]) { | |
138 | outrate = raw_sample_rate; | |
139 | break; | |
140 | } | |
141 | } | |
142 | // else find next highest sync sample rate | |
143 | while (!outrate && i >= 0) { | |
144 | if (supported_rates[i] > raw_sample_rate && supported_rates[i] % raw_sample_rate == 0) { | |
145 | outrate = supported_rates[i]; | |
146 | break; | |
147 | } | |
148 | i--; | |
149 | } | |
150 | } | |
151 | ||
152 | if (!outrate) { | |
153 | if (r->max_rate) { | |
154 | // resample to max rate for device | |
155 | outrate = supported_rates[0]; | |
156 | } else { | |
157 | // resample to max sync sample rate | |
158 | for (i = 0; supported_rates[i]; i++) { | |
159 | if (supported_rates[i] % raw_sample_rate == 0 || raw_sample_rate % supported_rates[i] == 0) { | |
160 | outrate = supported_rates[i]; | |
161 | break; | |
162 | } | |
163 | } | |
164 | } | |
165 | if (!outrate) { | |
166 | outrate = supported_rates[0]; | |
167 | } | |
168 | } | |
169 | ||
170 | process->in_sample_rate = raw_sample_rate; | |
171 | process->out_sample_rate = outrate; | |
172 | ||
173 | if (r->resampler) { | |
174 | SOXR(r, delete, r->resampler); | |
175 | r->resampler = NULL; | |
176 | } | |
177 | ||
178 | if (raw_sample_rate != outrate) { | |
179 | ||
180 | soxr_io_spec_t io_spec; | |
181 | soxr_quality_spec_t q_spec; | |
182 | soxr_error_t error; | |
183 | #if RESAMPLE_MP | |
184 | soxr_runtime_spec_t r_spec; | |
185 | #endif | |
186 | ||
187 | LOG_INFO("resampling from %u -> %u", raw_sample_rate, outrate); | |
188 | ||
189 | io_spec = SOXR(r, io_spec, SOXR_INT32_I, SOXR_INT32_I); | |
190 | io_spec.scale = r->scale; | |
191 | ||
192 | q_spec = SOXR(r, quality_spec, r->q_recipe, r->q_flags); | |
193 | if (r->q_precision > 0) { | |
194 | q_spec.precision = r->q_precision; | |
195 | } | |
196 | if (r->q_passband_end > 0) { | |
197 | q_spec.passband_end = r->q_passband_end; | |
198 | } | |
199 | if (r->q_stopband_begin > 0) { | |
200 | q_spec.stopband_begin = r->q_stopband_begin; | |
201 | } | |
202 | if (r->q_phase_response > -1) { | |
203 | q_spec.phase_response = r->q_phase_response; | |
204 | } | |
205 | ||
206 | #if RESAMPLE_MP | |
207 | r_spec = SOXR(r, runtime_spec, 0); // make use of libsoxr OpenMP support allowing parallel execution if multiple cores | |
208 | #endif | |
209 | ||
210 | LOG_DEBUG("resampling with soxr_quality_spec_t[precision: %03.1f, passband_end: %03.6f, stopband_begin: %03.6f, " | |
211 | "phase_response: %03.1f, flags: 0x%02x], soxr_io_spec_t[scale: %03.2f]", q_spec.precision, | |
212 | q_spec.passband_end, q_spec.stopband_begin, q_spec.phase_response, q_spec.flags, io_spec.scale); | |
213 | ||
214 | #if RESAMPLE_MP | |
215 | r->resampler = SOXR(r, create, raw_sample_rate, outrate, 2, &error, &io_spec, &q_spec, &r_spec); | |
216 | #else | |
217 | r->resampler = SOXR(r, create, raw_sample_rate, outrate, 2, &error, &io_spec, &q_spec, NULL); | |
218 | #endif | |
219 | ||
220 | if (error) { | |
221 | LOG_INFO("soxr_create error: %s", soxr_strerror(error)); | |
222 | return false; | |
223 | } | |
224 | ||
225 | r->old_clips = 0; | |
226 | return true; | |
227 | ||
228 | } else { | |
229 | ||
230 | LOG_INFO("disable resampling - rates match"); | |
231 | return false; | |
232 | } | |
233 | } | |
234 | ||
235 | void resample_flush(void) { | |
236 | if (r->resampler) { | |
237 | SOXR(r, delete, r->resampler); | |
238 | r->resampler = NULL; | |
239 | } | |
240 | } | |
241 | ||
242 | static bool load_soxr(void) { | |
243 | #if !LINKALL | |
244 | void *handle = dlopen(LIBSOXR, RTLD_NOW); | |
245 | char *err; | |
246 | ||
247 | if (!handle) { | |
248 | LOG_INFO("dlerror: %s", dlerror()); | |
249 | return false; | |
250 | } | |
251 | ||
252 | r->soxr_io_spec = dlsym(handle, "soxr_io_spec"); | |
253 | r->soxr_quality_spec = dlsym(handle, "soxr_quality_spec"); | |
254 | r->soxr_create = dlsym(handle, "soxr_create"); | |
255 | r->soxr_delete = dlsym(handle, "soxr_delete"); | |
256 | r->soxr_process = dlsym(handle, "soxr_process"); | |
257 | r->soxr_num_clips = dlsym(handle, "soxr_num_clips"); | |
258 | #if RESAMPLE_MP | |
259 | r->soxr_runtime_spec = dlsym(handle, "soxr_runtime_spec"); | |
260 | #endif | |
261 | ||
262 | if ((err = dlerror()) != NULL) { | |
263 | LOG_INFO("dlerror: %s", err); | |
264 | return false; | |
265 | } | |
266 | ||
267 | LOG_INFO("loaded "LIBSOXR); | |
268 | #endif | |
269 | ||
270 | return true; | |
271 | } | |
272 | ||
273 | bool resample_init(char *opt) { | |
274 | char *recipe = NULL, *flags = NULL; | |
275 | char *atten = NULL; | |
276 | char *precision = NULL, *passband_end = NULL, *stopband_begin = NULL, *phase_response = NULL; | |
277 | ||
278 | r = malloc(sizeof(struct soxr)); | |
279 | if (!r) { | |
280 | LOG_WARN("resampling disabled"); | |
281 | return false; | |
282 | } | |
283 | ||
284 | r->resampler = NULL; | |
285 | r->old_clips = 0; | |
286 | r->max_rate = false; | |
287 | r->exception = false; | |
288 | ||
289 | if (!load_soxr()) { | |
290 | LOG_WARN("resampling disabled"); | |
291 | return false; | |
292 | } | |
293 | ||
294 | if (opt) { | |
295 | recipe = next_param(opt, ':'); | |
296 | flags = next_param(NULL, ':'); | |
297 | atten = next_param(NULL, ':'); | |
298 | precision = next_param(NULL, ':'); | |
299 | passband_end = next_param(NULL, ':'); | |
300 | stopband_begin = next_param(NULL, ':'); | |
301 | phase_response = next_param(NULL, ':'); | |
302 | } | |
303 | ||
304 | // default to HQ (20 bit) if not user specified | |
305 | r->q_recipe = SOXR_HQ; | |
306 | r->q_flags = 0; | |
307 | // default to 1db of attenuation if not user specified | |
308 | r->scale = pow(10, -1.0 / 20); | |
309 | // override recipe derived values with user specified values | |
310 | r->q_precision = 0; | |
311 | r->q_passband_end = 0; | |
312 | r->q_stopband_begin = 0; | |
313 | r->q_phase_response = -1; | |
314 | ||
315 | if (recipe && recipe[0] != '\0') { | |
316 | if (strchr(recipe, 'v')) r->q_recipe = SOXR_VHQ; | |
317 | if (strchr(recipe, 'h')) r->q_recipe = SOXR_HQ; | |
318 | if (strchr(recipe, 'm')) r->q_recipe = SOXR_MQ; | |
319 | if (strchr(recipe, 'l')) r->q_recipe = SOXR_LQ; | |
320 | if (strchr(recipe, 'q')) r->q_recipe = SOXR_QQ; | |
321 | if (strchr(recipe, 'L')) r->q_recipe |= SOXR_LINEAR_PHASE; | |
322 | if (strchr(recipe, 'I')) r->q_recipe |= SOXR_INTERMEDIATE_PHASE; | |
323 | if (strchr(recipe, 'M')) r->q_recipe |= SOXR_MINIMUM_PHASE; | |
324 | if (strchr(recipe, 's')) r->q_recipe |= SOXR_STEEP_FILTER; | |
325 | // X = async resampling to max_rate | |
326 | if (strchr(recipe, 'X')) r->max_rate = true; | |
327 | // E = exception, only resample if native rate is not supported | |
328 | if (strchr(recipe, 'E')) r->exception = true; | |
329 | } | |
330 | ||
331 | if (flags) { | |
332 | r->q_flags = strtoul(flags, 0, 16); | |
333 | } | |
334 | ||
335 | if (atten) { | |
336 | double scale = pow(10, -atof(atten) / 20); | |
337 | if (scale > 0 && scale <= 1.0) { | |
338 | r->scale = scale; | |
339 | } | |
340 | } | |
341 | ||
342 | if (precision) { | |
343 | r->q_precision = atof(precision); | |
344 | } | |
345 | ||
346 | if (passband_end) { | |
347 | r->q_passband_end = atof(passband_end) / 100; | |
348 | } | |
349 | ||
350 | if (stopband_begin) { | |
351 | r->q_stopband_begin = atof(stopband_begin) / 100; | |
352 | } | |
353 | ||
354 | if (phase_response) { | |
355 | r->q_phase_response = atof(phase_response); | |
356 | } | |
357 | ||
358 | LOG_INFO("resampling %s recipe: 0x%02x, flags: 0x%02x, scale: %03.2f, precision: %03.1f, passband_end: %03.5f, stopband_begin: %03.5f, phase_response: %03.1f", | |
359 | r->max_rate ? "async" : "sync", | |
360 | r->q_recipe, r->q_flags, r->scale, r->q_precision, r->q_passband_end, r->q_stopband_begin, r->q_phase_response); | |
361 | ||
362 | return true; | |
363 | } | |
364 | ||
365 | #endif // #if RESAMPLE |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 "squeezelite.h" | |
21 | #include "slimproto.h" | |
22 | ||
23 | static log_level loglevel; | |
24 | ||
25 | #define PORT 3483 | |
26 | ||
27 | #define MAXBUF 4096 | |
28 | ||
29 | #if SL_LITTLE_ENDIAN | |
30 | #define LOCAL_PLAYER_IP 0x0100007f // 127.0.0.1 | |
31 | #define LOCAL_PLAYER_PORT 0x9b0d // 3483 | |
32 | #else | |
33 | #define LOCAL_PLAYER_IP 0x7f000001 // 127.0.0.1 | |
34 | #define LOCAL_PLAYER_PORT 0x0d9b // 3483 | |
35 | #endif | |
36 | ||
37 | static sockfd sock = -1; | |
38 | static in_addr_t slimproto_ip = 0; | |
39 | ||
40 | extern struct buffer *streambuf; | |
41 | extern struct buffer *outputbuf; | |
42 | ||
43 | extern struct streamstate stream; | |
44 | extern struct outputstate output; | |
45 | extern struct decodestate decode; | |
46 | ||
47 | extern struct codec *codecs[]; | |
48 | #if IR | |
49 | extern struct irstate ir; | |
50 | #endif | |
51 | ||
52 | event_event wake_e; | |
53 | ||
54 | #define LOCK_S mutex_lock(streambuf->mutex) | |
55 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
56 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
57 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
58 | #define LOCK_D mutex_lock(decode.mutex) | |
59 | #define UNLOCK_D mutex_unlock(decode.mutex) | |
60 | #if IR | |
61 | #define LOCK_I mutex_lock(ir.mutex) | |
62 | #define UNLOCK_I mutex_unlock(ir.mutex) | |
63 | #endif | |
64 | ||
65 | static struct { | |
66 | u32_t updated; | |
67 | u32_t stream_start; | |
68 | u32_t stream_full; | |
69 | u32_t stream_size; | |
70 | u64_t stream_bytes; | |
71 | u32_t output_full; | |
72 | u32_t output_size; | |
73 | u32_t frames_played; | |
74 | u32_t device_frames; | |
75 | u32_t current_sample_rate; | |
76 | u32_t last; | |
77 | stream_state stream_state; | |
78 | } status; | |
79 | ||
80 | int autostart; | |
81 | bool sentSTMu, sentSTMo, sentSTMl; | |
82 | u32_t new_server; | |
83 | char *new_server_cap; | |
84 | #define PLAYER_NAME_LEN 64 | |
85 | char player_name[PLAYER_NAME_LEN + 1] = ""; | |
86 | const char *name_file = NULL; | |
87 | ||
88 | void send_packet(u8_t *packet, size_t len) { | |
89 | u8_t *ptr = packet; | |
90 | unsigned try = 0; | |
91 | ssize_t n; | |
92 | ||
93 | while (len) { | |
94 | n = send(sock, ptr, len, MSG_NOSIGNAL); | |
95 | if (n <= 0) { | |
96 | if (n < 0 && last_error() == ERROR_WOULDBLOCK && try < 10) { | |
97 | LOG_DEBUG("retrying (%d) writing to socket", ++try); | |
98 | usleep(1000); | |
99 | continue; | |
100 | } | |
101 | LOG_INFO("failed writing to socket: %s", strerror(last_error())); | |
102 | return; | |
103 | } | |
104 | ptr += n; | |
105 | len -= n; | |
106 | } | |
107 | } | |
108 | ||
109 | static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap, u8_t mac[6]) { | |
110 | const char *base_cap = "Model=squeezelite,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Firmware=" VERSION; | |
111 | struct HELO_packet pkt; | |
112 | ||
113 | memset(&pkt, 0, sizeof(pkt)); | |
114 | memcpy(&pkt.opcode, "HELO", 4); | |
115 | pkt.length = htonl(sizeof(struct HELO_packet) - 8 + strlen(base_cap) + strlen(fixed_cap) + strlen(var_cap)); | |
116 | pkt.deviceid = 12; // squeezeplay | |
117 | pkt.revision = 0; | |
118 | packn(&pkt.wlan_channellist, reconnect ? 0x4000 : 0x0000); | |
119 | packN(&pkt.bytes_received_H, (u64_t)status.stream_bytes >> 32); | |
120 | packN(&pkt.bytes_received_L, (u64_t)status.stream_bytes & 0xffffffff); | |
121 | memcpy(pkt.mac, mac, 6); | |
122 | ||
123 | LOG_INFO("mac: %02x:%02x:%02x:%02x:%02x:%02x", pkt.mac[0], pkt.mac[1], pkt.mac[2], pkt.mac[3], pkt.mac[4], pkt.mac[5]); | |
124 | ||
125 | LOG_INFO("cap: %s%s%s", base_cap, fixed_cap, var_cap); | |
126 | ||
127 | send_packet((u8_t *)&pkt, sizeof(pkt)); | |
128 | send_packet((u8_t *)base_cap, strlen(base_cap)); | |
129 | send_packet((u8_t *)fixed_cap, strlen(fixed_cap)); | |
130 | send_packet((u8_t *)var_cap, strlen(var_cap)); | |
131 | } | |
132 | ||
133 | static void sendSTAT(const char *event, u32_t server_timestamp) { | |
134 | struct STAT_packet pkt; | |
135 | u32_t now = gettime_ms(); | |
136 | u32_t ms_played; | |
137 | ||
138 | if (status.current_sample_rate && status.frames_played && status.frames_played > status.device_frames) { | |
139 | ms_played = (u32_t)(((u64_t)(status.frames_played - status.device_frames) * (u64_t)1000) / (u64_t)status.current_sample_rate); | |
140 | if (now > status.updated) ms_played += (now - status.updated); | |
141 | LOG_SDEBUG("ms_played: %u (frames_played: %u device_frames: %u)", ms_played, status.frames_played, status.device_frames); | |
142 | } else if (status.frames_played && now > status.stream_start) { | |
143 | ms_played = now - status.stream_start; | |
144 | LOG_SDEBUG("ms_played: %u using elapsed time (frames_played: %u device_frames: %u)", ms_played, status.frames_played, status.device_frames); | |
145 | } else { | |
146 | LOG_SDEBUG("ms_played: 0"); | |
147 | ms_played = 0; | |
148 | } | |
149 | ||
150 | memset(&pkt, 0, sizeof(struct STAT_packet)); | |
151 | memcpy(&pkt.opcode, "STAT", 4); | |
152 | pkt.length = htonl(sizeof(struct STAT_packet) - 8); | |
153 | memcpy(&pkt.event, event, 4); | |
154 | // num_crlf | |
155 | // mas_initialized; mas_mode; | |
156 | packN(&pkt.stream_buffer_fullness, status.stream_full); | |
157 | packN(&pkt.stream_buffer_size, status.stream_size); | |
158 | packN(&pkt.bytes_received_H, (u64_t)status.stream_bytes >> 32); | |
159 | packN(&pkt.bytes_received_L, (u64_t)status.stream_bytes & 0xffffffff); | |
160 | pkt.signal_strength = 0xffff; | |
161 | packN(&pkt.jiffies, now); | |
162 | packN(&pkt.output_buffer_size, status.output_size); | |
163 | packN(&pkt.output_buffer_fullness, status.output_full); | |
164 | packN(&pkt.elapsed_seconds, ms_played / 1000); | |
165 | // voltage; | |
166 | packN(&pkt.elapsed_milliseconds, ms_played); | |
167 | pkt.server_timestamp = server_timestamp; // keep this is server format - don't unpack/pack | |
168 | // error_code; | |
169 | ||
170 | LOG_DEBUG("STAT: %s", event); | |
171 | ||
172 | if (loglevel == lSDEBUG) { | |
173 | LOG_SDEBUG("received bytesL: %u streambuf: %u outputbuf: %u calc elapsed: %u real elapsed: %u (diff: %d) device: %u delay: %d", | |
174 | (u32_t)status.stream_bytes, status.stream_full, status.output_full, ms_played, now - status.stream_start, | |
175 | ms_played - now + status.stream_start, status.device_frames * 1000 / status.current_sample_rate, now - status.updated); | |
176 | } | |
177 | ||
178 | send_packet((u8_t *)&pkt, sizeof(pkt)); | |
179 | } | |
180 | ||
181 | static void sendDSCO(disconnect_code disconnect) { | |
182 | struct DSCO_packet pkt; | |
183 | ||
184 | memset(&pkt, 0, sizeof(pkt)); | |
185 | memcpy(&pkt.opcode, "DSCO", 4); | |
186 | pkt.length = htonl(sizeof(pkt) - 8); | |
187 | pkt.reason = disconnect & 0xFF; | |
188 | ||
189 | LOG_DEBUG("DSCO: %d", disconnect); | |
190 | ||
191 | send_packet((u8_t *)&pkt, sizeof(pkt)); | |
192 | } | |
193 | ||
194 | static void sendRESP(const char *header, size_t len) { | |
195 | struct RESP_header pkt_header; | |
196 | ||
197 | memset(&pkt_header, 0, sizeof(pkt_header)); | |
198 | memcpy(&pkt_header.opcode, "RESP", 4); | |
199 | pkt_header.length = htonl(sizeof(pkt_header) + len - 8); | |
200 | ||
201 | LOG_DEBUG("RESP"); | |
202 | ||
203 | send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); | |
204 | send_packet((u8_t *)header, len); | |
205 | } | |
206 | ||
207 | static void sendMETA(const char *meta, size_t len) { | |
208 | struct META_header pkt_header; | |
209 | ||
210 | memset(&pkt_header, 0, sizeof(pkt_header)); | |
211 | memcpy(&pkt_header.opcode, "META", 4); | |
212 | pkt_header.length = htonl(sizeof(pkt_header) + len - 8); | |
213 | ||
214 | LOG_DEBUG("META"); | |
215 | ||
216 | send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); | |
217 | send_packet((u8_t *)meta, len); | |
218 | } | |
219 | ||
220 | static void sendSETDName(const char *name) { | |
221 | struct SETD_header pkt_header; | |
222 | ||
223 | memset(&pkt_header, 0, sizeof(pkt_header)); | |
224 | memcpy(&pkt_header.opcode, "SETD", 4); | |
225 | ||
226 | pkt_header.id = 0; // id 0 is playername S:P:Squeezebox2 | |
227 | pkt_header.length = htonl(sizeof(pkt_header) + strlen(name) + 1 - 8); | |
228 | ||
229 | LOG_DEBUG("set playername: %s", name); | |
230 | ||
231 | send_packet((u8_t *)&pkt_header, sizeof(pkt_header)); | |
232 | send_packet((u8_t *)name, strlen(name) + 1); | |
233 | } | |
234 | ||
235 | #if IR | |
236 | void sendIR(u32_t code, u32_t ts) { | |
237 | struct IR_packet pkt; | |
238 | ||
239 | memset(&pkt, 0, sizeof(pkt)); | |
240 | memcpy(&pkt.opcode, "IR ", 4); | |
241 | pkt.length = htonl(sizeof(pkt) - 8); | |
242 | ||
243 | packN(&pkt.jiffies, ts); | |
244 | pkt.ir_code = htonl(code); | |
245 | ||
246 | LOG_DEBUG("IR: ir code: 0x%x ts: %u", code, ts); | |
247 | ||
248 | send_packet((u8_t *)&pkt, sizeof(pkt)); | |
249 | } | |
250 | #endif | |
251 | ||
252 | static void process_strm(u8_t *pkt, int len) { | |
253 | struct strm_packet *strm = (struct strm_packet *)pkt; | |
254 | ||
255 | LOG_DEBUG("strm command %c", strm->command); | |
256 | ||
257 | switch(strm->command) { | |
258 | case 't': | |
259 | sendSTAT("STMt", strm->replay_gain); // STMt replay_gain is no longer used to track latency, but support it | |
260 | break; | |
261 | case 'q': | |
262 | decode_flush(); | |
263 | output_flush(); | |
264 | status.frames_played = 0; | |
265 | stream_disconnect(); | |
266 | sendSTAT("STMf", 0); | |
267 | buf_flush(streambuf); | |
268 | break; | |
269 | case 'f': | |
270 | decode_flush(); | |
271 | output_flush(); | |
272 | status.frames_played = 0; | |
273 | if (stream_disconnect()) { | |
274 | sendSTAT("STMf", 0); | |
275 | } | |
276 | buf_flush(streambuf); | |
277 | break; | |
278 | case 'p': | |
279 | { | |
280 | unsigned interval = unpackN(&strm->replay_gain); | |
281 | LOCK_O; | |
282 | output.pause_frames = interval * status.current_sample_rate / 1000; | |
283 | if (interval) { | |
284 | output.state = OUTPUT_PAUSE_FRAMES; | |
285 | } else { | |
286 | output.state = OUTPUT_STOPPED; | |
287 | output.stop_time = gettime_ms(); | |
288 | } | |
289 | UNLOCK_O; | |
290 | if (!interval) sendSTAT("STMp", 0); | |
291 | LOG_DEBUG("pause interval: %u", interval); | |
292 | } | |
293 | break; | |
294 | case 'a': | |
295 | { | |
296 | unsigned interval = unpackN(&strm->replay_gain); | |
297 | LOCK_O; | |
298 | output.skip_frames = interval * status.current_sample_rate / 1000; | |
299 | output.state = OUTPUT_SKIP_FRAMES; | |
300 | UNLOCK_O; | |
301 | LOG_DEBUG("skip ahead interval: %u", interval); | |
302 | } | |
303 | break; | |
304 | case 'u': | |
305 | { | |
306 | unsigned jiffies = unpackN(&strm->replay_gain); | |
307 | LOCK_O; | |
308 | output.state = jiffies ? OUTPUT_START_AT : OUTPUT_RUNNING; | |
309 | output.start_at = jiffies; | |
310 | UNLOCK_O; | |
311 | LOG_DEBUG("unpause at: %u now: %u", jiffies, gettime_ms()); | |
312 | sendSTAT("STMr", 0); | |
313 | } | |
314 | break; | |
315 | case 's': | |
316 | { | |
317 | unsigned header_len = len - sizeof(struct strm_packet); | |
318 | char *header = (char *)(pkt + sizeof(struct strm_packet)); | |
319 | in_addr_t ip = (in_addr_t)strm->server_ip; // keep in network byte order | |
320 | u16_t port = strm->server_port; // keep in network byte order | |
321 | if (ip == 0) ip = slimproto_ip; | |
322 | ||
323 | LOG_DEBUG("strm s autostart: %c transition period: %u transition type: %u codec: %c", | |
324 | strm->autostart, strm->transition_period, strm->transition_type - '0', strm->format); | |
325 | ||
326 | autostart = strm->autostart - '0'; | |
327 | sendSTAT("STMf", 0); | |
328 | if (header_len > MAX_HEADER -1) { | |
329 | LOG_WARN("header too long: %u", header_len); | |
330 | break; | |
331 | } | |
332 | if (strm->format != '?') { | |
333 | codec_open(strm->format, strm->pcm_sample_size, strm->pcm_sample_rate, strm->pcm_channels, strm->pcm_endianness); | |
334 | } else if (autostart >= 2) { | |
335 | // extension to slimproto to allow server to detect codec from response header and send back in codc message | |
336 | LOG_DEBUG("streaming unknown codec"); | |
337 | } else { | |
338 | LOG_WARN("unknown codec requires autostart >= 2"); | |
339 | break; | |
340 | } | |
341 | if (ip == LOCAL_PLAYER_IP && port == LOCAL_PLAYER_PORT) { | |
342 | // extension to slimproto for LocalPlayer - header is filename not http header, don't expect cont | |
343 | stream_file(header, header_len, strm->threshold * 1024); | |
344 | autostart -= 2; | |
345 | } else { | |
346 | stream_sock(ip, port, header, header_len, strm->threshold * 1024, autostart >= 2); | |
347 | } | |
348 | sendSTAT("STMc", 0); | |
349 | sentSTMu = sentSTMo = sentSTMl = false; | |
350 | LOCK_O; | |
351 | output.threshold = strm->output_threshold; | |
352 | output.next_replay_gain = unpackN(&strm->replay_gain); | |
353 | output.fade_mode = strm->transition_type - '0'; | |
354 | output.fade_secs = strm->transition_period; | |
355 | output.invert = (strm->flags & 0x03) == 0x03; | |
356 | LOG_DEBUG("set fade mode: %u", output.fade_mode); | |
357 | UNLOCK_O; | |
358 | } | |
359 | break; | |
360 | default: | |
361 | LOG_WARN("unhandled strm %c", strm->command); | |
362 | break; | |
363 | } | |
364 | } | |
365 | ||
366 | static void process_cont(u8_t *pkt, int len) { | |
367 | struct cont_packet *cont = (struct cont_packet *)pkt; | |
368 | cont->metaint = unpackN(&cont->metaint); | |
369 | ||
370 | LOG_DEBUG("cont metaint: %u loop: %u", cont->metaint, cont->loop); | |
371 | ||
372 | if (autostart > 1) { | |
373 | autostart -= 2; | |
374 | LOCK_S; | |
375 | if (stream.state == STREAMING_WAIT) { | |
376 | stream.state = STREAMING_BUFFERING; | |
377 | stream.meta_interval = stream.meta_next = cont->metaint; | |
378 | } | |
379 | UNLOCK_S; | |
380 | wake_controller(); | |
381 | } | |
382 | } | |
383 | ||
384 | static void process_codc(u8_t *pkt, int len) { | |
385 | struct codc_packet *codc = (struct codc_packet *)pkt; | |
386 | ||
387 | LOG_DEBUG("codc: %c", codc->format); | |
388 | codec_open(codc->format, codc->pcm_sample_size, codc->pcm_sample_rate, codc->pcm_channels, codc->pcm_endianness); | |
389 | } | |
390 | ||
391 | static void process_aude(u8_t *pkt, int len) { | |
392 | struct aude_packet *aude = (struct aude_packet *)pkt; | |
393 | ||
394 | LOG_DEBUG("enable spdif: %d dac: %d", aude->enable_spdif, aude->enable_dac); | |
395 | ||
396 | LOCK_O; | |
397 | if (!aude->enable_spdif && output.state != OUTPUT_OFF) { | |
398 | output.state = OUTPUT_OFF; | |
399 | } | |
400 | if (aude->enable_spdif && output.state == OUTPUT_OFF && !output.idle_to) { | |
401 | output.state = OUTPUT_STOPPED; | |
402 | output.stop_time = gettime_ms(); | |
403 | } | |
404 | UNLOCK_O; | |
405 | } | |
406 | ||
407 | static void process_audg(u8_t *pkt, int len) { | |
408 | struct audg_packet *audg = (struct audg_packet *)pkt; | |
409 | audg->gainL = unpackN(&audg->gainL); | |
410 | audg->gainR = unpackN(&audg->gainR); | |
411 | ||
412 | LOG_DEBUG("audg gainL: %u gainR: %u adjust: %u", audg->gainL, audg->gainR, audg->adjust); | |
413 | ||
414 | set_volume(audg->adjust ? audg->gainL : FIXED_ONE, audg->adjust ? audg->gainR : FIXED_ONE); | |
415 | } | |
416 | ||
417 | static void process_setd(u8_t *pkt, int len) { | |
418 | struct setd_packet *setd = (struct setd_packet *)pkt; | |
419 | ||
420 | // handle player name query and change | |
421 | if (setd->id == 0) { | |
422 | if (len == 5) { | |
423 | if (strlen(player_name)) { | |
424 | sendSETDName(player_name); | |
425 | } | |
426 | } else if (len > 5) { | |
427 | strncpy(player_name, setd->data, PLAYER_NAME_LEN); | |
428 | player_name[PLAYER_NAME_LEN] = '\0'; | |
429 | LOG_INFO("set name: %s", setd->data); | |
430 | // confirm change to server | |
431 | sendSETDName(setd->data); | |
432 | // write name to name_file if -N option set | |
433 | if (name_file) { | |
434 | FILE *fp = fopen(name_file, "w"); | |
435 | if (fp) { | |
436 | LOG_INFO("storing name in %s", name_file); | |
437 | fputs(player_name, fp); | |
438 | fclose(fp); | |
439 | } else { | |
440 | LOG_WARN("unable to store new name in %s", name_file); | |
441 | } | |
442 | } | |
443 | } | |
444 | } | |
445 | } | |
446 | ||
447 | #define SYNC_CAP ",SyncgroupID=" | |
448 | #define SYNC_CAP_LEN 13 | |
449 | ||
450 | static void process_serv(u8_t *pkt, int len) { | |
451 | struct serv_packet *serv = (struct serv_packet *)pkt; | |
452 | ||
453 | LOG_INFO("switch server"); | |
454 | ||
455 | new_server = serv->server_ip; | |
456 | ||
457 | if (len - sizeof(struct serv_packet) == 10) { | |
458 | if (!new_server_cap) { | |
459 | new_server_cap = malloc(SYNC_CAP_LEN + 10 + 1); | |
460 | } | |
461 | new_server_cap[0] = '\0'; | |
462 | strcat(new_server_cap, SYNC_CAP); | |
463 | strncat(new_server_cap, (const char *)(pkt + sizeof(struct serv_packet)), 10); | |
464 | } else { | |
465 | if (new_server_cap) { | |
466 | free(new_server_cap); | |
467 | new_server_cap = NULL; | |
468 | } | |
469 | } | |
470 | } | |
471 | ||
472 | struct handler { | |
473 | char opcode[5]; | |
474 | void (*handler)(u8_t *, int); | |
475 | }; | |
476 | ||
477 | static struct handler handlers[] = { | |
478 | { "strm", process_strm }, | |
479 | { "cont", process_cont }, | |
480 | { "codc", process_codc }, | |
481 | { "aude", process_aude }, | |
482 | { "audg", process_audg }, | |
483 | { "setd", process_setd }, | |
484 | { "serv", process_serv }, | |
485 | { "", NULL }, | |
486 | }; | |
487 | ||
488 | static void process(u8_t *pack, int len) { | |
489 | struct handler *h = handlers; | |
490 | while (h->handler && strncmp((char *)pack, h->opcode, 4)) { h++; } | |
491 | ||
492 | if (h->handler) { | |
493 | LOG_DEBUG("%s", h->opcode); | |
494 | h->handler(pack, len); | |
495 | } else { | |
496 | pack[4] = '\0'; | |
497 | LOG_WARN("unhandled %s", (char *)pack); | |
498 | } | |
499 | } | |
500 | ||
501 | static bool running; | |
502 | ||
503 | static void slimproto_run() { | |
504 | static u8_t buffer[MAXBUF]; | |
505 | int expect = 0; | |
506 | int got = 0; | |
507 | u32_t now; | |
508 | static u32_t last = 0; | |
509 | event_handle ehandles[2]; | |
510 | int timeouts = 0; | |
511 | ||
512 | set_readwake_handles(ehandles, sock, wake_e); | |
513 | ||
514 | while (running && !new_server) { | |
515 | ||
516 | bool wake = false; | |
517 | event_type ev; | |
518 | ||
519 | if ((ev = wait_readwake(ehandles, 1000)) != EVENT_TIMEOUT) { | |
520 | ||
521 | if (ev == EVENT_READ) { | |
522 | ||
523 | if (expect > 0) { | |
524 | int n = recv(sock, buffer + got, expect, 0); | |
525 | if (n <= 0) { | |
526 | if (n < 0 && last_error() == ERROR_WOULDBLOCK) { | |
527 | continue; | |
528 | } | |
529 | LOG_INFO("error reading from socket: %s", n ? strerror(last_error()) : "closed"); | |
530 | return; | |
531 | } | |
532 | expect -= n; | |
533 | got += n; | |
534 | if (expect == 0) { | |
535 | process(buffer, got); | |
536 | got = 0; | |
537 | } | |
538 | } else if (expect == 0) { | |
539 | int n = recv(sock, buffer + got, 2 - got, 0); | |
540 | if (n <= 0) { | |
541 | if (n < 0 && last_error() == ERROR_WOULDBLOCK) { | |
542 | continue; | |
543 | } | |
544 | LOG_INFO("error reading from socket: %s", n ? strerror(last_error()) : "closed"); | |
545 | return; | |
546 | } | |
547 | got += n; | |
548 | if (got == 2) { | |
549 | expect = buffer[0] << 8 | buffer[1]; // length pack 'n' | |
550 | got = 0; | |
551 | if (expect > MAXBUF) { | |
552 | LOG_ERROR("FATAL: slimproto packet too big: %d > %d", expect, MAXBUF); | |
553 | return; | |
554 | } | |
555 | } | |
556 | } else { | |
557 | LOG_ERROR("FATAL: negative expect"); | |
558 | return; | |
559 | } | |
560 | ||
561 | } | |
562 | ||
563 | if (ev == EVENT_WAKE) { | |
564 | wake = true; | |
565 | } | |
566 | ||
567 | timeouts = 0; | |
568 | ||
569 | } else if (++timeouts > 35) { | |
570 | ||
571 | // expect message from server every 5 seconds, but 30 seconds on mysb.com so timeout after 35 seconds | |
572 | LOG_INFO("No messages from server - connection dead"); | |
573 | return; | |
574 | } | |
575 | ||
576 | // update playback state when woken or every 100ms | |
577 | now = gettime_ms(); | |
578 | ||
579 | if (wake || now - last > 100 || last > now) { | |
580 | bool _sendSTMs = false; | |
581 | bool _sendDSCO = false; | |
582 | bool _sendRESP = false; | |
583 | bool _sendMETA = false; | |
584 | bool _sendSTMd = false; | |
585 | bool _sendSTMt = false; | |
586 | bool _sendSTMl = false; | |
587 | bool _sendSTMu = false; | |
588 | bool _sendSTMo = false; | |
589 | bool _sendSTMn = false; | |
590 | bool _stream_disconnect = false; | |
591 | bool _start_output = false; | |
592 | decode_state _decode_state; | |
593 | disconnect_code disconnect_code; | |
594 | static char header[MAX_HEADER]; | |
595 | size_t header_len = 0; | |
596 | #if IR | |
597 | bool _sendIR = false; | |
598 | u32_t ir_code, ir_ts; | |
599 | #endif | |
600 | last = now; | |
601 | ||
602 | LOCK_S; | |
603 | status.stream_full = _buf_used(streambuf); | |
604 | status.stream_size = streambuf->size; | |
605 | status.stream_bytes = stream.bytes; | |
606 | status.stream_state = stream.state; | |
607 | ||
608 | if (stream.state == DISCONNECT) { | |
609 | disconnect_code = stream.disconnect; | |
610 | stream.state = STOPPED; | |
611 | _sendDSCO = true; | |
612 | } | |
613 | if (!stream.sent_headers && | |
614 | (stream.state == STREAMING_HTTP || stream.state == STREAMING_WAIT || stream.state == STREAMING_BUFFERING)) { | |
615 | header_len = stream.header_len; | |
616 | memcpy(header, stream.header, header_len); | |
617 | _sendRESP = true; | |
618 | stream.sent_headers = true; | |
619 | } | |
620 | if (stream.meta_send) { | |
621 | header_len = stream.header_len; | |
622 | memcpy(header, stream.header, header_len); | |
623 | _sendMETA = true; | |
624 | stream.meta_send = false; | |
625 | } | |
626 | UNLOCK_S; | |
627 | ||
628 | LOCK_D; | |
629 | if ((status.stream_state == STREAMING_HTTP || status.stream_state == STREAMING_FILE) && !sentSTMl | |
630 | && decode.state == DECODE_READY) { | |
631 | if (autostart == 0) { | |
632 | decode.state = DECODE_RUNNING; | |
633 | _sendSTMl = true; | |
634 | sentSTMl = true; | |
635 | } else if (autostart == 1) { | |
636 | decode.state = DECODE_RUNNING; | |
637 | _start_output = true; | |
638 | } | |
639 | // autostart 2 and 3 require cont to be received first | |
640 | } | |
641 | if (decode.state == DECODE_COMPLETE || decode.state == DECODE_ERROR) { | |
642 | if (decode.state == DECODE_COMPLETE) _sendSTMd = true; | |
643 | if (decode.state == DECODE_ERROR) _sendSTMn = true; | |
644 | decode.state = DECODE_STOPPED; | |
645 | if (status.stream_state == STREAMING_HTTP || status.stream_state == STREAMING_FILE) { | |
646 | _stream_disconnect = true; | |
647 | } | |
648 | } | |
649 | _decode_state = decode.state; | |
650 | UNLOCK_D; | |
651 | ||
652 | LOCK_O; | |
653 | status.output_full = _buf_used(outputbuf); | |
654 | status.output_size = outputbuf->size; | |
655 | status.frames_played = output.frames_played_dmp; | |
656 | status.current_sample_rate = output.current_sample_rate; | |
657 | status.updated = output.updated; | |
658 | status.device_frames = output.device_frames; | |
659 | ||
660 | if (output.track_started) { | |
661 | _sendSTMs = true; | |
662 | output.track_started = false; | |
663 | status.stream_start = output.track_start_time; | |
664 | } | |
665 | #if PORTAUDIO | |
666 | if (output.pa_reopen) { | |
667 | _pa_open(); | |
668 | output.pa_reopen = false; | |
669 | } | |
670 | #endif | |
671 | if (_start_output && (output.state == OUTPUT_STOPPED || OUTPUT_OFF)) { | |
672 | output.state = OUTPUT_BUFFER; | |
673 | } | |
674 | if (output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT && | |
675 | _decode_state == DECODE_STOPPED) { | |
676 | _sendSTMu = true; | |
677 | sentSTMu = true; | |
678 | LOG_DEBUG("output underrun"); | |
679 | output.state = OUTPUT_STOPPED; | |
680 | output.stop_time = now; | |
681 | } | |
682 | if (output.state == OUTPUT_RUNNING && !sentSTMo && status.output_full == 0 && status.stream_state == STREAMING_HTTP) { | |
683 | _sendSTMo = true; | |
684 | sentSTMo = true; | |
685 | } | |
686 | if (output.state == OUTPUT_STOPPED && output.idle_to && (now - output.stop_time > output.idle_to)) { | |
687 | output.state = OUTPUT_OFF; | |
688 | LOG_DEBUG("output timeout"); | |
689 | } | |
690 | if (output.state == OUTPUT_RUNNING && now - status.last > 1000) { | |
691 | _sendSTMt = true; | |
692 | status.last = now; | |
693 | } | |
694 | UNLOCK_O; | |
695 | ||
696 | #if IR | |
697 | LOCK_I; | |
698 | if (ir.code) { | |
699 | _sendIR = true; | |
700 | ir_code = ir.code; | |
701 | ir_ts = ir.ts; | |
702 | ir.code = 0; | |
703 | } | |
704 | UNLOCK_I; | |
705 | #endif | |
706 | ||
707 | if (_stream_disconnect) stream_disconnect(); | |
708 | ||
709 | // send packets once locks released as packet sending can block | |
710 | if (_sendDSCO) sendDSCO(disconnect_code); | |
711 | if (_sendSTMs) sendSTAT("STMs", 0); | |
712 | if (_sendSTMd) sendSTAT("STMd", 0); | |
713 | if (_sendSTMt) sendSTAT("STMt", 0); | |
714 | if (_sendSTMl) sendSTAT("STMl", 0); | |
715 | if (_sendSTMu) sendSTAT("STMu", 0); | |
716 | if (_sendSTMo) sendSTAT("STMo", 0); | |
717 | if (_sendSTMn) sendSTAT("STMn", 0); | |
718 | if (_sendRESP) sendRESP(header, header_len); | |
719 | if (_sendMETA) sendMETA(header, header_len); | |
720 | #if IR | |
721 | if (_sendIR) sendIR(ir_code, ir_ts); | |
722 | #endif | |
723 | } | |
724 | } | |
725 | } | |
726 | ||
727 | // called from other threads to wake state machine above | |
728 | void wake_controller(void) { | |
729 | wake_signal(wake_e); | |
730 | } | |
731 | ||
732 | in_addr_t discover_server(void) { | |
733 | struct sockaddr_in d; | |
734 | struct sockaddr_in s; | |
735 | char *buf; | |
736 | struct pollfd pollinfo; | |
737 | ||
738 | int disc_sock = socket(AF_INET, SOCK_DGRAM, 0); | |
739 | ||
740 | socklen_t enable = 1; | |
741 | setsockopt(disc_sock, SOL_SOCKET, SO_BROADCAST, (const void *)&enable, sizeof(enable)); | |
742 | ||
743 | buf = "e"; | |
744 | ||
745 | memset(&d, 0, sizeof(d)); | |
746 | d.sin_family = AF_INET; | |
747 | d.sin_port = htons(PORT); | |
748 | d.sin_addr.s_addr = htonl(INADDR_BROADCAST); | |
749 | ||
750 | pollinfo.fd = disc_sock; | |
751 | pollinfo.events = POLLIN; | |
752 | ||
753 | do { | |
754 | ||
755 | LOG_INFO("sending discovery"); | |
756 | memset(&s, 0, sizeof(s)); | |
757 | ||
758 | if (sendto(disc_sock, buf, 1, 0, (struct sockaddr *)&d, sizeof(d)) < 0) { | |
759 | LOG_INFO("error sending disovery"); | |
760 | } | |
761 | ||
762 | if (poll(&pollinfo, 1, 5000) == 1) { | |
763 | char readbuf[10]; | |
764 | socklen_t slen = sizeof(s); | |
765 | recvfrom(disc_sock, readbuf, 10, 0, (struct sockaddr *)&s, &slen); | |
766 | LOG_INFO("got response from: %s:%d", inet_ntoa(s.sin_addr), ntohs(s.sin_port)); | |
767 | } | |
768 | ||
769 | } while (s.sin_addr.s_addr == 0 && running); | |
770 | ||
771 | closesocket(disc_sock); | |
772 | ||
773 | return s.sin_addr.s_addr; | |
774 | } | |
775 | ||
776 | #define FIXED_CAP_LEN 256 | |
777 | #define VAR_CAP_LEN 128 | |
778 | ||
779 | void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile, const char *modelname) { | |
780 | struct sockaddr_in serv_addr; | |
781 | static char fixed_cap[FIXED_CAP_LEN], var_cap[VAR_CAP_LEN] = ""; | |
782 | bool reconnect = false; | |
783 | unsigned failed_connect = 0; | |
784 | unsigned slimproto_port = 0; | |
785 | int i; | |
786 | ||
787 | wake_create(wake_e); | |
788 | ||
789 | loglevel = level; | |
790 | running = true; | |
791 | ||
792 | if (server) { | |
793 | server_addr(server, &slimproto_ip, &slimproto_port); | |
794 | } | |
795 | ||
796 | if (!slimproto_ip) { | |
797 | slimproto_ip = discover_server(); | |
798 | } | |
799 | ||
800 | if (!slimproto_port) { | |
801 | slimproto_port = PORT; | |
802 | } | |
803 | ||
804 | if (name) { | |
805 | strncpy(player_name, name, PLAYER_NAME_LEN); | |
806 | player_name[PLAYER_NAME_LEN] = '\0'; | |
807 | } | |
808 | ||
809 | if (namefile) { | |
810 | FILE *fp; | |
811 | name_file = namefile; | |
812 | fp = fopen(namefile, "r"); | |
813 | if (fp) { | |
814 | if (!fgets(player_name, PLAYER_NAME_LEN, fp)) { | |
815 | player_name[PLAYER_NAME_LEN] = '\0'; | |
816 | } else { | |
817 | // strip any \n from fgets response | |
818 | int len = strlen(player_name); | |
819 | if (len > 0 && player_name[len - 1] == '\n') { | |
820 | player_name[len - 1] = '\0'; | |
821 | } | |
822 | LOG_INFO("retrieved name %s from %s", player_name, name_file); | |
823 | } | |
824 | fclose(fp); | |
825 | } | |
826 | } | |
827 | ||
828 | if (!running) return; | |
829 | ||
830 | LOCK_O; | |
831 | snprintf(fixed_cap, FIXED_CAP_LEN, ",ModelName=%s,MaxSampleRate=%u", modelname ? modelname : MODEL_NAME_STRING, | |
832 | output.supported_rates[0]); | |
833 | ||
834 | for (i = 0; i < MAX_CODECS; i++) { | |
835 | if (codecs[i] && codecs[i]->id && strlen(fixed_cap) < FIXED_CAP_LEN - 10) { | |
836 | strcat(fixed_cap, ","); | |
837 | strcat(fixed_cap, codecs[i]->types); | |
838 | } | |
839 | } | |
840 | UNLOCK_O; | |
841 | ||
842 | memset(&serv_addr, 0, sizeof(serv_addr)); | |
843 | serv_addr.sin_family = AF_INET; | |
844 | serv_addr.sin_addr.s_addr = slimproto_ip; | |
845 | serv_addr.sin_port = htons(slimproto_port); | |
846 | ||
847 | LOG_INFO("connecting to %s:%d", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port)); | |
848 | ||
849 | new_server = 0; | |
850 | ||
851 | while (running) { | |
852 | ||
853 | if (new_server) { | |
854 | slimproto_ip = serv_addr.sin_addr.s_addr = new_server; | |
855 | LOG_INFO("switching server to %s:%d", inet_ntoa(serv_addr.sin_addr), ntohs(serv_addr.sin_port)); | |
856 | new_server = 0; | |
857 | reconnect = false; | |
858 | } | |
859 | ||
860 | sock = socket(AF_INET, SOCK_STREAM, 0); | |
861 | ||
862 | set_nonblock(sock); | |
863 | set_nosigpipe(sock); | |
864 | ||
865 | if (connect_timeout(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr), 5) != 0) { | |
866 | ||
867 | LOG_INFO("unable to connect to server %u", failed_connect); | |
868 | sleep(5); | |
869 | ||
870 | // rediscover server if it was not set at startup | |
871 | if (!server && ++failed_connect > 5) { | |
872 | slimproto_ip = serv_addr.sin_addr.s_addr = discover_server(); | |
873 | } | |
874 | ||
875 | } else { | |
876 | ||
877 | struct sockaddr_in our_addr; | |
878 | socklen_t len; | |
879 | ||
880 | LOG_INFO("connected"); | |
881 | ||
882 | var_cap[0] = '\0'; | |
883 | failed_connect = 0; | |
884 | ||
885 | // check if this is a local player now we are connected & signal to server via 'loc' format | |
886 | // this requires LocalPlayer server plugin to enable direct file access | |
887 | len = sizeof(our_addr); | |
888 | getsockname(sock, (struct sockaddr *) &our_addr, &len); | |
889 | ||
890 | if (our_addr.sin_addr.s_addr == serv_addr.sin_addr.s_addr) { | |
891 | LOG_INFO("local player"); | |
892 | strcat(var_cap, ",loc"); | |
893 | } | |
894 | ||
895 | // add on any capablity to be sent to the new server | |
896 | if (new_server_cap) { | |
897 | strcat(var_cap, new_server_cap); | |
898 | free(new_server_cap); | |
899 | new_server_cap = NULL; | |
900 | } | |
901 | ||
902 | sendHELO(reconnect, fixed_cap, var_cap, mac); | |
903 | ||
904 | slimproto_run(); | |
905 | ||
906 | if (!reconnect) { | |
907 | reconnect = true; | |
908 | } | |
909 | ||
910 | usleep(100000); | |
911 | } | |
912 | ||
913 | closesocket(sock); | |
914 | } | |
915 | } | |
916 | ||
917 | void slimproto_stop(void) { | |
918 | LOG_INFO("slimproto stop"); | |
919 | running = false; | |
920 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // packet formats for slimproto | |
21 | ||
22 | #pragma pack(push, 1) | |
23 | ||
24 | // from S:N:Slimproto _hello_handler | |
25 | struct HELO_packet { | |
26 | char opcode[4]; | |
27 | u32_t length; | |
28 | u8_t deviceid; | |
29 | u8_t revision; | |
30 | u8_t mac[6]; | |
31 | u8_t uuid[16]; | |
32 | u16_t wlan_channellist; | |
33 | u32_t bytes_received_H, bytes_received_L; | |
34 | char lang[2]; | |
35 | // u8_t capabilities[]; | |
36 | }; | |
37 | ||
38 | // S:N:Slimproto _stat_handler | |
39 | struct STAT_packet { | |
40 | char opcode[4]; | |
41 | u32_t length; | |
42 | u32_t event; | |
43 | u8_t num_crlf; | |
44 | u8_t mas_initialized; | |
45 | u8_t mas_mode; | |
46 | u32_t stream_buffer_size; | |
47 | u32_t stream_buffer_fullness; | |
48 | u32_t bytes_received_H; | |
49 | u32_t bytes_received_L; | |
50 | u16_t signal_strength; | |
51 | u32_t jiffies; | |
52 | u32_t output_buffer_size; | |
53 | u32_t output_buffer_fullness; | |
54 | u32_t elapsed_seconds; | |
55 | u16_t voltage; | |
56 | u32_t elapsed_milliseconds; | |
57 | u32_t server_timestamp; | |
58 | u16_t error_code; | |
59 | }; | |
60 | ||
61 | // S:N:Slimproto _disco_handler | |
62 | struct DSCO_packet { | |
63 | char opcode[4]; | |
64 | u32_t length; | |
65 | u8_t reason; | |
66 | }; | |
67 | ||
68 | // S:N:Slimproto _http_response_handler | |
69 | struct RESP_header { | |
70 | char opcode[4]; | |
71 | u32_t length; | |
72 | // char header[] - added in sendRESP | |
73 | }; | |
74 | ||
75 | // S:N:Slimproto _http_metadata_handler | |
76 | struct META_header { | |
77 | char opcode[4]; | |
78 | u32_t length; | |
79 | // char metadata[] | |
80 | }; | |
81 | ||
82 | // S:N:Slimproto _http_setting_handler | |
83 | struct SETD_header { | |
84 | char opcode[4]; | |
85 | u32_t length; | |
86 | u8_t id; | |
87 | // data | |
88 | }; | |
89 | ||
90 | #if IR | |
91 | struct IR_packet { | |
92 | char opcode[4]; | |
93 | u32_t length; | |
94 | u32_t jiffies; | |
95 | u8_t format; // ignored by server | |
96 | u8_t bits; // ignored by server | |
97 | u32_t ir_code; | |
98 | }; | |
99 | #endif | |
100 | ||
101 | // from S:P:Squeezebox stream_s | |
102 | struct strm_packet { | |
103 | char opcode[4]; | |
104 | char command; | |
105 | u8_t autostart; | |
106 | u8_t format; | |
107 | u8_t pcm_sample_size; | |
108 | u8_t pcm_sample_rate; | |
109 | u8_t pcm_channels; | |
110 | u8_t pcm_endianness; | |
111 | u8_t threshold; | |
112 | u8_t spdif_enable; | |
113 | u8_t transition_period; | |
114 | u8_t transition_type; | |
115 | u8_t flags; | |
116 | u8_t output_threshold; | |
117 | u8_t slaves; | |
118 | u32_t replay_gain; | |
119 | u16_t server_port; | |
120 | u32_t server_ip; | |
121 | //char request_string[]; | |
122 | }; | |
123 | ||
124 | // S:P:Squeezebox2 | |
125 | struct aude_packet { | |
126 | char opcode[4]; | |
127 | u8_t enable_spdif; | |
128 | u8_t enable_dac; | |
129 | }; | |
130 | ||
131 | // S:P:Squeezebox2 | |
132 | struct audg_packet { | |
133 | char opcode[4]; | |
134 | u32_t old_gainL; // unused | |
135 | u32_t old_gainR; // unused | |
136 | u8_t adjust; | |
137 | u8_t preamp; // unused | |
138 | u32_t gainL; | |
139 | u32_t gainR; | |
140 | // squence ids - unused | |
141 | }; | |
142 | ||
143 | // S:P:Squeezebox2 | |
144 | struct cont_packet { | |
145 | char opcode[4]; | |
146 | u32_t metaint; | |
147 | u8_t loop; | |
148 | // guids we don't use | |
149 | }; | |
150 | ||
151 | // S:C:Commands | |
152 | struct serv_packet { | |
153 | char opcode[4]; | |
154 | u32_t server_ip; | |
155 | // possible sync group | |
156 | }; | |
157 | ||
158 | // S:P:Squeezebox2 | |
159 | struct setd_packet { | |
160 | char opcode[4]; | |
161 | u8_t id; | |
162 | char data[]; | |
163 | }; | |
164 | ||
165 | // codec open - this is an extension to slimproto to allow the server to read the header and then return decode params | |
166 | struct codc_packet { | |
167 | char opcode[4]; | |
168 | u8_t format; | |
169 | u8_t pcm_sample_size; | |
170 | u8_t pcm_sample_rate; | |
171 | u8_t pcm_channels; | |
172 | u8_t pcm_endianness; | |
173 | }; | |
174 | ||
175 | #pragma pack(pop) |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // make may define: PORTAUDIO, SELFPIPE, RESAMPLE, RESAMPLE_MP, VISEXPORT, IR, DSD, LINKALL to influence build | |
21 | ||
22 | #define VERSION "v1.8" | |
23 | ||
24 | #if !defined(MODEL_NAME) | |
25 | #define MODEL_NAME SqueezeLite | |
26 | #endif | |
27 | ||
28 | #define QUOTE(name) #name | |
29 | #define STR(macro) QUOTE(macro) | |
30 | #define MODEL_NAME_STRING STR(MODEL_NAME) | |
31 | ||
32 | // build detection | |
33 | #if defined(linux) | |
34 | #define LINUX 1 | |
35 | #define OSX 0 | |
36 | #define WIN 0 | |
37 | #define FREEBSD 0 | |
38 | #elif defined (__APPLE__) | |
39 | #define LINUX 0 | |
40 | #define OSX 1 | |
41 | #define WIN 0 | |
42 | #define FREEBSD 0 | |
43 | #elif defined (_MSC_VER) | |
44 | #define LINUX 0 | |
45 | #define OSX 0 | |
46 | #define WIN 1 | |
47 | #define FREEBSD 0 | |
48 | #elif defined(__FreeBSD__) | |
49 | #define LINUX 0 | |
50 | #define OSX 0 | |
51 | #define WIN 0 | |
52 | #define FREEBSD 1 | |
53 | #else | |
54 | #error unknown target | |
55 | #endif | |
56 | ||
57 | #if LINUX && !defined(PORTAUDIO) | |
58 | #define ALSA 1 | |
59 | #define PORTAUDIO 0 | |
60 | #else | |
61 | #define ALSA 0 | |
62 | #define PORTAUDIO 1 | |
63 | #endif | |
64 | ||
65 | #if LINUX && !defined(SELFPIPE) | |
66 | #define EVENTFD 1 | |
67 | #define SELFPIPE 0 | |
68 | #define WINEVENT 0 | |
69 | #endif | |
70 | #if (LINUX && !EVENTFD) || OSX || FREEBSD | |
71 | #define EVENTFD 0 | |
72 | #define SELFPIPE 1 | |
73 | #define WINEVENT 0 | |
74 | #endif | |
75 | #if WIN | |
76 | #define EVENTFD 0 | |
77 | #define SELFPIPE 0 | |
78 | #define WINEVENT 1 | |
79 | #endif | |
80 | ||
81 | #if defined(RESAMPLE) || defined(RESAMPLE_MP) | |
82 | #undef RESAMPLE | |
83 | #define RESAMPLE 1 // resampling | |
84 | #define PROCESS 1 // any sample processing (only resampling at present) | |
85 | #else | |
86 | #define RESAMPLE 0 | |
87 | #define PROCESS 0 | |
88 | #endif | |
89 | #if defined(RESAMPLE_MP) | |
90 | #undef RESAMPLE_MP | |
91 | #define RESAMPLE_MP 1 | |
92 | #else | |
93 | #define RESAMPLE_MP 0 | |
94 | #endif | |
95 | ||
96 | #if defined(FFMPEG) | |
97 | #undef FFMPEG | |
98 | #define FFMPEG 1 | |
99 | #else | |
100 | #define FFMPEG 0 | |
101 | #endif | |
102 | ||
103 | #if LINUX && defined(VISEXPORT) | |
104 | #undef VISEXPORT | |
105 | #define VISEXPORT 1 // visulizer export support uses linux shared memory | |
106 | #else | |
107 | #define VISEXPORT 0 | |
108 | #endif | |
109 | ||
110 | #if LINUX && defined(IR) | |
111 | #undef IR | |
112 | #define IR 1 | |
113 | #else | |
114 | #define IR 0 | |
115 | #endif | |
116 | ||
117 | #if defined(DSD) | |
118 | #undef DSD | |
119 | #define DSD 1 | |
120 | #define IF_DSD(x) { x } | |
121 | #else | |
122 | #undef DSD | |
123 | #define DSD 0 | |
124 | #define IF_DSD(x) | |
125 | #endif | |
126 | ||
127 | #if defined(LINKALL) | |
128 | #undef LINKALL | |
129 | #define LINKALL 1 // link all libraries at build time - requires all to be available at run time | |
130 | #else | |
131 | #define LINKALL 0 | |
132 | #endif | |
133 | ||
134 | ||
135 | #if !LINKALL | |
136 | ||
137 | // dynamically loaded libraries at run time | |
138 | ||
139 | #if LINUX | |
140 | #define LIBFLAC "libFLAC.so.8" | |
141 | #define LIBMAD "libmad.so.0" | |
142 | #define LIBMPG "libmpg123.so.0" | |
143 | #define LIBVORBIS "libvorbisfile.so.3" | |
144 | #define LIBTREMOR "libvorbisidec.so.1" | |
145 | #define LIBFAAD "libfaad.so.2" | |
146 | #define LIBAVUTIL "libavutil.so.%d" | |
147 | #define LIBAVCODEC "libavcodec.so.%d" | |
148 | #define LIBAVFORMAT "libavformat.so.%d" | |
149 | #define LIBSOXR "libsoxr.so.0" | |
150 | #define LIBLIRC "liblirc_client.so.0" | |
151 | #endif | |
152 | ||
153 | #if OSX | |
154 | #define LIBFLAC "libFLAC.8.dylib" | |
155 | #define LIBMAD "libmad.0.dylib" | |
156 | #define LIBMPG "libmpg123.0.dylib" | |
157 | #define LIBVORBIS "libvorbisfile.3.dylib" | |
158 | #define LIBTREMOR "libvorbisidec.1.dylib" | |
159 | #define LIBFAAD "libfaad.2.dylib" | |
160 | #define LIBAVUTIL "libavutil.%d.dylib" | |
161 | #define LIBAVCODEC "libavcodec.%d.dylib" | |
162 | #define LIBAVFORMAT "libavformat.%d.dylib" | |
163 | #define LIBSOXR "libsoxr.0.dylib" | |
164 | #endif | |
165 | ||
166 | #if WIN | |
167 | #define LIBFLAC "libFLAC.dll" | |
168 | #define LIBMAD "libmad-0.dll" | |
169 | #define LIBMPG "libmpg123-0.dll" | |
170 | #define LIBVORBIS "libvorbisfile.dll" | |
171 | #define LIBTREMOR "libvorbisidec.dll" | |
172 | #define LIBFAAD "libfaad2.dll" | |
173 | #define LIBAVUTIL "avutil-%d.dll" | |
174 | #define LIBAVCODEC "avcodec-%d.dll" | |
175 | #define LIBAVFORMAT "avformat-%d.dll" | |
176 | #define LIBSOXR "libsoxr.dll" | |
177 | #endif | |
178 | ||
179 | #if FREEBSD | |
180 | #define LIBFLAC "libFLAC.so.11" | |
181 | #define LIBMAD "libmad.so.2" | |
182 | #define LIBMPG "libmpg123.so.0" | |
183 | #define LIBVORBIS "libvorbisfile.so.6" | |
184 | #define LIBTREMOR "libvorbisidec.so.1" | |
185 | #define LIBFAAD "libfaad.so.2" | |
186 | #define LIBAVUTIL "libavutil.so.%d" | |
187 | #define LIBAVCODEC "libavcodec.so.%d" | |
188 | #define LIBAVFORMAT "libavformat.so.%d" | |
189 | #endif | |
190 | ||
191 | #endif // !LINKALL | |
192 | ||
193 | // config options | |
194 | #define STREAMBUF_SIZE (2 * 1024 * 1024) | |
195 | #define OUTPUTBUF_SIZE (44100 * 8 * 10) | |
196 | #define OUTPUTBUF_SIZE_CROSSFADE (OUTPUTBUF_SIZE * 12 / 10) | |
197 | ||
198 | #define MAX_HEADER 4096 // do not reduce as icy-meta max is 4080 | |
199 | ||
200 | #if ALSA | |
201 | #define ALSA_BUFFER_TIME 40 | |
202 | #define ALSA_PERIOD_COUNT 4 | |
203 | #define OUTPUT_RT_PRIORITY 45 | |
204 | #endif | |
205 | ||
206 | #define SL_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) | |
207 | ||
208 | #include <stdio.h> | |
209 | #include <stdlib.h> | |
210 | #include <stdarg.h> | |
211 | #include <string.h> | |
212 | #include <errno.h> | |
213 | #include <limits.h> | |
214 | #include <sys/types.h> | |
215 | ||
216 | #if LINUX || OSX || FREEBSD | |
217 | #include <unistd.h> | |
218 | #include <stdbool.h> | |
219 | #include <netinet/in.h> | |
220 | #include <arpa/inet.h> | |
221 | #include <sys/time.h> | |
222 | #include <sys/socket.h> | |
223 | #include <poll.h> | |
224 | #include <dlfcn.h> | |
225 | #include <pthread.h> | |
226 | #include <signal.h> | |
227 | ||
228 | #define STREAM_THREAD_STACK_SIZE 64 * 1024 | |
229 | #define DECODE_THREAD_STACK_SIZE 128 * 1024 | |
230 | #define OUTPUT_THREAD_STACK_SIZE 64 * 1024 | |
231 | #define IR_THREAD_STACK_SIZE 64 * 1024 | |
232 | #define thread_t pthread_t; | |
233 | #define closesocket(s) close(s) | |
234 | #define last_error() errno | |
235 | #define ERROR_WOULDBLOCK EWOULDBLOCK | |
236 | ||
237 | typedef u_int8_t u8_t; | |
238 | typedef u_int16_t u16_t; | |
239 | typedef u_int32_t u32_t; | |
240 | typedef u_int64_t u64_t; | |
241 | typedef int16_t s16_t; | |
242 | typedef int32_t s32_t; | |
243 | typedef int64_t s64_t; | |
244 | ||
245 | #define mutex_type pthread_mutex_t | |
246 | #define mutex_create(m) pthread_mutex_init(&m, NULL) | |
247 | #define mutex_create_p(m) pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT); pthread_mutex_init(&m, &attr); pthread_mutexattr_destroy(&attr) | |
248 | #define mutex_lock(m) pthread_mutex_lock(&m) | |
249 | #define mutex_unlock(m) pthread_mutex_unlock(&m) | |
250 | #define mutex_destroy(m) pthread_mutex_destroy(&m) | |
251 | #define thread_type pthread_t | |
252 | ||
253 | #endif | |
254 | ||
255 | #if WIN | |
256 | ||
257 | #include <winsock2.h> | |
258 | #include <ws2tcpip.h> | |
259 | #include <io.h> | |
260 | ||
261 | #define STREAM_THREAD_STACK_SIZE (1024 * 64) | |
262 | #define DECODE_THREAD_STACK_SIZE (1024 * 128) | |
263 | #define OUTPUT_THREAD_STACK_SIZE (1024 * 64) | |
264 | ||
265 | typedef unsigned __int8 u8_t; | |
266 | typedef unsigned __int16 u16_t; | |
267 | typedef unsigned __int32 u32_t; | |
268 | typedef unsigned __int64 u64_t; | |
269 | typedef __int16 s16_t; | |
270 | typedef __int32 s32_t; | |
271 | typedef __int64 s64_t; | |
272 | ||
273 | typedef BOOL bool; | |
274 | #define true TRUE | |
275 | #define false FALSE | |
276 | ||
277 | #define inline __inline | |
278 | ||
279 | #define mutex_type HANDLE | |
280 | #define mutex_create(m) m = CreateMutex(NULL, FALSE, NULL) | |
281 | #define mutex_create_p mutex_create | |
282 | #define mutex_lock(m) WaitForSingleObject(m, INFINITE) | |
283 | #define mutex_unlock(m) ReleaseMutex(m) | |
284 | #define mutex_destroy(m) CloseHandle(m) | |
285 | #define thread_type HANDLE | |
286 | ||
287 | #define usleep(x) Sleep(x/1000) | |
288 | #define sleep(x) Sleep(x*1000) | |
289 | #define last_error() WSAGetLastError() | |
290 | #define ERROR_WOULDBLOCK WSAEWOULDBLOCK | |
291 | #define open _open | |
292 | #define read _read | |
293 | #define snprintf _snprintf | |
294 | ||
295 | #define in_addr_t u32_t | |
296 | #define socklen_t int | |
297 | #define ssize_t int | |
298 | ||
299 | #define RTLD_NOW 0 | |
300 | ||
301 | #endif | |
302 | ||
303 | #if !defined(MSG_NOSIGNAL) | |
304 | #define MSG_NOSIGNAL 0 | |
305 | #endif | |
306 | ||
307 | typedef u32_t frames_t; | |
308 | typedef int sockfd; | |
309 | ||
310 | #if EVENTFD | |
311 | #include <sys/eventfd.h> | |
312 | #define event_event int | |
313 | #define event_handle struct pollfd | |
314 | #define wake_create(e) e = eventfd(0, 0) | |
315 | #define wake_signal(e) eventfd_write(e, 1) | |
316 | #define wake_clear(e) eventfd_t val; eventfd_read(e, &val) | |
317 | #define wake_close(e) close(e) | |
318 | #endif | |
319 | ||
320 | #if SELFPIPE | |
321 | #define event_handle struct pollfd | |
322 | #define event_event struct wake | |
323 | #define wake_create(e) pipe(e.fds); set_nonblock(e.fds[0]); set_nonblock(e.fds[1]) | |
324 | #define wake_signal(e) write(e.fds[1], ".", 1) | |
325 | #define wake_clear(e) char c[10]; read(e, &c, 10) | |
326 | #define wake_close(e) close(e.fds[0]); close(e.fds[1]) | |
327 | struct wake { | |
328 | int fds[2]; | |
329 | }; | |
330 | #endif | |
331 | ||
332 | #if WINEVENT | |
333 | #define event_event HANDLE | |
334 | #define event_handle HANDLE | |
335 | #define wake_create(e) e = CreateEvent(NULL, FALSE, FALSE, NULL) | |
336 | #define wake_signal(e) SetEvent(e) | |
337 | #define wake_close(e) CloseHandle(e) | |
338 | #endif | |
339 | ||
340 | // printf/scanf formats for u64_t | |
341 | #if (LINUX && __WORDSIZE == 64) || (FREEBSD && __LP64__) | |
342 | #define FMT_u64 "%lu" | |
343 | #define FMT_x64 "%lx" | |
344 | #elif __GLIBC_HAVE_LONG_LONG || defined __GNUC__ || WIN | |
345 | #define FMT_u64 "%llu" | |
346 | #define FMT_x64 "%llx" | |
347 | #else | |
348 | #error can not support u64_t | |
349 | #endif | |
350 | ||
351 | #define MAX_SILENCE_FRAMES 2048 | |
352 | ||
353 | #define FIXED_ONE 0x10000 | |
354 | ||
355 | #define BYTES_PER_FRAME 8 | |
356 | ||
357 | #define min(a,b) (((a) < (b)) ? (a) : (b)) | |
358 | ||
359 | // logging | |
360 | typedef enum { lERROR = 0, lWARN, lINFO, lDEBUG, lSDEBUG } log_level; | |
361 | ||
362 | const char *logtime(void); | |
363 | void logprint(const char *fmt, ...); | |
364 | ||
365 | #define LOG_ERROR(fmt, ...) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__) | |
366 | #define LOG_WARN(fmt, ...) if (loglevel >= lWARN) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__) | |
367 | #define LOG_INFO(fmt, ...) if (loglevel >= lINFO) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__) | |
368 | #define LOG_DEBUG(fmt, ...) if (loglevel >= lDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__) | |
369 | #define LOG_SDEBUG(fmt, ...) if (loglevel >= lSDEBUG) logprint("%s %s:%d " fmt "\n", logtime(), __FUNCTION__, __LINE__, ##__VA_ARGS__) | |
370 | ||
371 | // utils.c (non logging) | |
372 | typedef enum { EVENT_TIMEOUT = 0, EVENT_READ, EVENT_WAKE } event_type; | |
373 | ||
374 | char *next_param(char *src, char c); | |
375 | u32_t gettime_ms(void); | |
376 | void get_mac(u8_t *mac); | |
377 | void set_nonblock(sockfd s); | |
378 | int connect_timeout(sockfd sock, const struct sockaddr *addr, socklen_t addrlen, int timeout); | |
379 | void server_addr(char *server, in_addr_t *ip_ptr, unsigned *port_ptr); | |
380 | void set_readwake_handles(event_handle handles[], sockfd s, event_event e); | |
381 | event_type wait_readwake(event_handle handles[], int timeout); | |
382 | void packN(u32_t *dest, u32_t val); | |
383 | void packn(u16_t *dest, u16_t val); | |
384 | u32_t unpackN(u32_t *src); | |
385 | u16_t unpackn(u16_t *src); | |
386 | #if OSX | |
387 | void set_nosigpipe(sockfd s); | |
388 | #else | |
389 | #define set_nosigpipe(s) | |
390 | #endif | |
391 | #if WIN | |
392 | void winsock_init(void); | |
393 | void winsock_close(void); | |
394 | void *dlopen(const char *filename, int flag); | |
395 | void *dlsym(void *handle, const char *symbol); | |
396 | char *dlerror(void); | |
397 | int poll(struct pollfd *fds, unsigned long numfds, int timeout); | |
398 | #endif | |
399 | #if LINUX || FREEBSD | |
400 | void touch_memory(u8_t *buf, size_t size); | |
401 | #endif | |
402 | ||
403 | // buffer.c | |
404 | struct buffer { | |
405 | u8_t *buf; | |
406 | u8_t *readp; | |
407 | u8_t *writep; | |
408 | u8_t *wrap; | |
409 | size_t size; | |
410 | size_t base_size; | |
411 | mutex_type mutex; | |
412 | }; | |
413 | ||
414 | // _* called with mutex locked | |
415 | unsigned _buf_used(struct buffer *buf); | |
416 | unsigned _buf_space(struct buffer *buf); | |
417 | unsigned _buf_cont_read(struct buffer *buf); | |
418 | unsigned _buf_cont_write(struct buffer *buf); | |
419 | void _buf_inc_readp(struct buffer *buf, unsigned by); | |
420 | void _buf_inc_writep(struct buffer *buf, unsigned by); | |
421 | void buf_flush(struct buffer *buf); | |
422 | void buf_adjust(struct buffer *buf, size_t mod); | |
423 | void _buf_resize(struct buffer *buf, size_t size); | |
424 | void buf_init(struct buffer *buf, size_t size); | |
425 | void buf_destroy(struct buffer *buf); | |
426 | ||
427 | // slimproto.c | |
428 | void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile, const char *modelname); | |
429 | void slimproto_stop(void); | |
430 | void wake_controller(void); | |
431 | ||
432 | // stream.c | |
433 | typedef enum { STOPPED = 0, DISCONNECT, STREAMING_WAIT, | |
434 | STREAMING_BUFFERING, STREAMING_FILE, STREAMING_HTTP, SEND_HEADERS, RECV_HEADERS } stream_state; | |
435 | typedef enum { DISCONNECT_OK = 0, LOCAL_DISCONNECT = 1, REMOTE_DISCONNECT = 2, UNREACHABLE = 3, TIMEOUT = 4 } disconnect_code; | |
436 | ||
437 | struct streamstate { | |
438 | stream_state state; | |
439 | disconnect_code disconnect; | |
440 | char *header; | |
441 | size_t header_len; | |
442 | bool sent_headers; | |
443 | bool cont_wait; | |
444 | u64_t bytes; | |
445 | unsigned threshold; | |
446 | u32_t meta_interval; | |
447 | u32_t meta_next; | |
448 | u32_t meta_left; | |
449 | bool meta_send; | |
450 | }; | |
451 | ||
452 | void stream_init(log_level level, unsigned stream_buf_size); | |
453 | void stream_close(void); | |
454 | void stream_file(const char *header, size_t header_len, unsigned threshold); | |
455 | void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, unsigned threshold, bool cont_wait); | |
456 | bool stream_disconnect(void); | |
457 | ||
458 | // decode.c | |
459 | typedef enum { DECODE_STOPPED = 0, DECODE_READY, DECODE_RUNNING, DECODE_COMPLETE, DECODE_ERROR } decode_state; | |
460 | ||
461 | struct decodestate { | |
462 | decode_state state; | |
463 | bool new_stream; | |
464 | mutex_type mutex; | |
465 | #if PROCESS | |
466 | bool direct; | |
467 | bool process; | |
468 | #endif | |
469 | }; | |
470 | ||
471 | #if PROCESS | |
472 | struct processstate { | |
473 | u8_t *inbuf, *outbuf; | |
474 | unsigned max_in_frames, max_out_frames; | |
475 | unsigned in_frames, out_frames; | |
476 | unsigned in_sample_rate, out_sample_rate; | |
477 | unsigned long total_in, total_out; | |
478 | }; | |
479 | #endif | |
480 | ||
481 | struct codec { | |
482 | char id; | |
483 | char *types; | |
484 | unsigned min_read_bytes; | |
485 | unsigned min_space; | |
486 | void (*open)(u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness); | |
487 | void (*close)(void); | |
488 | decode_state (*decode)(void); | |
489 | }; | |
490 | ||
491 | void decode_init(log_level level, const char *include_codecs, const char *exclude_codecs); | |
492 | void decode_close(void); | |
493 | void decode_flush(void); | |
494 | unsigned decode_newstream(unsigned sample_rate, unsigned supported_rates[]); | |
495 | void codec_open(u8_t format, u8_t sample_size, u8_t sample_rate, u8_t channels, u8_t endianness); | |
496 | ||
497 | #if PROCESS | |
498 | // process.c | |
499 | void process_samples(void); | |
500 | void process_drain(void); | |
501 | void process_flush(void); | |
502 | unsigned process_newstream(bool *direct, unsigned raw_sample_rate, unsigned supported_rates[]); | |
503 | void process_init(char *opt); | |
504 | #endif | |
505 | ||
506 | #if RESAMPLE | |
507 | // resample.c | |
508 | void resample_samples(struct processstate *process); | |
509 | bool resample_drain(struct processstate *process); | |
510 | bool resample_newstream(struct processstate *process, unsigned raw_sample_rate, unsigned supported_rates[]); | |
511 | void resample_flush(void); | |
512 | bool resample_init(char *opt); | |
513 | #endif | |
514 | ||
515 | // output.c output_alsa.c output_pa.c output_pack.c | |
516 | typedef enum { OUTPUT_OFF = -1, OUTPUT_STOPPED = 0, OUTPUT_BUFFER, OUTPUT_RUNNING, | |
517 | OUTPUT_PAUSE_FRAMES, OUTPUT_SKIP_FRAMES, OUTPUT_START_AT } output_state; | |
518 | ||
519 | typedef enum { S32_LE, S24_LE, S24_3LE, S16_LE } output_format; | |
520 | ||
521 | typedef enum { FADE_INACTIVE = 0, FADE_DUE, FADE_ACTIVE } fade_state; | |
522 | typedef enum { FADE_UP = 1, FADE_DOWN, FADE_CROSS } fade_dir; | |
523 | typedef enum { FADE_NONE = 0, FADE_CROSSFADE, FADE_IN, FADE_OUT, FADE_INOUT } fade_mode; | |
524 | ||
525 | #define MAX_SUPPORTED_SAMPLERATES 16 | |
526 | #define TEST_RATES = { 384000, 352800, 192000, 176400, 96000, 88200, 48000, 44100, 32000, 24000, 22500, 16000, 12000, 11025, 8000, 0 } | |
527 | ||
528 | struct outputstate { | |
529 | output_state state; | |
530 | output_format format; | |
531 | const char *device; | |
532 | #if ALSA | |
533 | unsigned buffer; | |
534 | unsigned period; | |
535 | #endif | |
536 | bool track_started; | |
537 | #if PORTAUDIO | |
538 | bool pa_reopen; | |
539 | unsigned latency; | |
540 | int osx_playnice; | |
541 | #endif | |
542 | int (* write_cb)(frames_t out_frames, bool silence, s32_t gainL, s32_t gainR, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr); | |
543 | unsigned start_frames; | |
544 | unsigned frames_played; | |
545 | unsigned frames_played_dmp;// frames played at the point delay is measured | |
546 | unsigned current_sample_rate; | |
547 | unsigned supported_rates[MAX_SUPPORTED_SAMPLERATES]; // ordered largest first so [0] is max_rate | |
548 | unsigned default_sample_rate; | |
549 | bool error_opening; | |
550 | unsigned device_frames; | |
551 | u32_t updated; | |
552 | u32_t track_start_time; | |
553 | u32_t current_replay_gain; | |
554 | union { | |
555 | u32_t pause_frames; | |
556 | u32_t skip_frames; | |
557 | u32_t start_at; | |
558 | }; | |
559 | unsigned next_sample_rate; // set in decode thread | |
560 | u8_t *track_start; // set in decode thread | |
561 | u32_t gainL; // set by slimproto | |
562 | u32_t gainR; // set by slimproto | |
563 | bool invert; // set by slimproto | |
564 | u32_t next_replay_gain; // set by slimproto | |
565 | unsigned threshold; // set by slimproto | |
566 | fade_state fade; | |
567 | u8_t *fade_start; | |
568 | u8_t *fade_end; | |
569 | fade_dir fade_dir; | |
570 | fade_mode fade_mode; // set by slimproto | |
571 | unsigned fade_secs; // set by slimproto | |
572 | unsigned rate_delay; | |
573 | bool delay_active; | |
574 | u32_t stop_time; | |
575 | u32_t idle_to; | |
576 | #if DSD | |
577 | bool next_dop; // set in decode thread | |
578 | bool dop; | |
579 | bool has_dop; // set in dop_init - output device supports dop | |
580 | unsigned dop_delay; // set in dop_init - delay in ms switching to/from dop | |
581 | #endif | |
582 | }; | |
583 | ||
584 | void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[], unsigned idle); | |
585 | void output_close_common(void); | |
586 | void output_flush(void); | |
587 | // _* called with mutex locked | |
588 | frames_t _output_frames(frames_t avail); | |
589 | void _checkfade(bool); | |
590 | ||
591 | // output_alsa.c | |
592 | #if ALSA | |
593 | void list_devices(void); | |
594 | void list_mixers(const char *output_device); | |
595 | void set_volume(unsigned left, unsigned right); | |
596 | bool test_open(const char *device, unsigned rates[]); | |
597 | void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], | |
598 | unsigned rate_delay, unsigned rt_priority, unsigned idle, char *volume_mixer, bool mixer_unmute); | |
599 | void output_close_alsa(void); | |
600 | #endif | |
601 | ||
602 | // output_pa.c | |
603 | #if PORTAUDIO | |
604 | void list_devices(void); | |
605 | void set_volume(unsigned left, unsigned right); | |
606 | bool test_open(const char *device, unsigned rates[]); | |
607 | void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle); | |
608 | void output_close_pa(void); | |
609 | void _pa_open(void); | |
610 | #endif | |
611 | ||
612 | // output_stdout.c | |
613 | void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay); | |
614 | void output_close_stdout(void); | |
615 | ||
616 | // output_pack.c | |
617 | void _scale_and_pack_frames(void *outputptr, s32_t *inputptr, frames_t cnt, s32_t gainL, s32_t gainR, output_format format); | |
618 | void _apply_cross(struct buffer *outputbuf, frames_t out_frames, s32_t cross_gain_in, s32_t cross_gain_out, s32_t **cross_ptr); | |
619 | void _apply_gain(struct buffer *outputbuf, frames_t count, s32_t gainL, s32_t gainR); | |
620 | s32_t gain(s32_t gain, s32_t sample); | |
621 | s32_t to_gain(float f); | |
622 | ||
623 | // output_vis.c | |
624 | #if VISEXPORT | |
625 | void _vis_export(struct buffer *outputbuf, struct outputstate *output, frames_t out_frames, bool silence); | |
626 | void output_vis_init(log_level level, u8_t *mac); | |
627 | void vis_stop(void); | |
628 | #else | |
629 | #define _vis_export(...) | |
630 | #define vis_stop() | |
631 | #endif | |
632 | ||
633 | // dop.c | |
634 | #if DSD | |
635 | bool is_flac_dop(u32_t *lptr, u32_t *rptr, frames_t frames); | |
636 | void update_dop(u32_t *ptr, frames_t frames, bool invert); | |
637 | void dop_silence_frames(u32_t *ptr, frames_t frames); | |
638 | void dop_init(bool enable, unsigned delay); | |
639 | #endif | |
640 | ||
641 | // codecs | |
642 | #define MAX_CODECS 9 | |
643 | ||
644 | struct codec *register_flac(void); | |
645 | struct codec *register_pcm(void); | |
646 | struct codec *register_mad(void); | |
647 | struct codec *register_mpg(void); | |
648 | struct codec *register_vorbis(void); | |
649 | struct codec *register_faad(void); | |
650 | struct codec *register_dsd(void); | |
651 | struct codec *register_ff(const char *codec); | |
652 | ||
653 | // ir.c | |
654 | #if IR | |
655 | struct irstate { | |
656 | mutex_type mutex; | |
657 | u32_t code; | |
658 | u32_t ts; | |
659 | }; | |
660 | ||
661 | void ir_init(log_level level, char *lircrc); | |
662 | void ir_close(void); | |
663 | #endif |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 | // stream thread | |
21 | ||
22 | #include "squeezelite.h" | |
23 | ||
24 | #include <fcntl.h> | |
25 | ||
26 | static log_level loglevel; | |
27 | ||
28 | static struct buffer buf; | |
29 | struct buffer *streambuf = &buf; | |
30 | ||
31 | #define LOCK mutex_lock(streambuf->mutex) | |
32 | #define UNLOCK mutex_unlock(streambuf->mutex) | |
33 | ||
34 | static sockfd fd; | |
35 | ||
36 | struct streamstate stream; | |
37 | ||
38 | static void send_header(void) { | |
39 | char *ptr = stream.header; | |
40 | int len = stream.header_len; | |
41 | ||
42 | unsigned try = 0; | |
43 | ssize_t n; | |
44 | ||
45 | while (len) { | |
46 | n = send(fd, ptr, len, MSG_NOSIGNAL); | |
47 | if (n <= 0) { | |
48 | if (n < 0 && last_error() == ERROR_WOULDBLOCK && try < 10) { | |
49 | LOG_SDEBUG("retrying (%d) writing to socket", ++try); | |
50 | usleep(1000); | |
51 | continue; | |
52 | } | |
53 | LOG_INFO("failed writing to socket: %s", strerror(last_error())); | |
54 | stream.disconnect = LOCAL_DISCONNECT; | |
55 | stream.state = DISCONNECT; | |
56 | wake_controller(); | |
57 | return; | |
58 | } | |
59 | LOG_SDEBUG("wrote %d bytes to socket", n); | |
60 | ptr += n; | |
61 | len -= n; | |
62 | } | |
63 | LOG_SDEBUG("wrote header"); | |
64 | } | |
65 | ||
66 | static bool running = true; | |
67 | ||
68 | static void _disconnect(stream_state state, disconnect_code disconnect) { | |
69 | stream.state = state; | |
70 | stream.disconnect = disconnect; | |
71 | closesocket(fd); | |
72 | fd = -1; | |
73 | wake_controller(); | |
74 | } | |
75 | ||
76 | static void *stream_thread() { | |
77 | ||
78 | while (running) { | |
79 | ||
80 | struct pollfd pollinfo; | |
81 | size_t space; | |
82 | ||
83 | LOCK; | |
84 | ||
85 | space = min(_buf_space(streambuf), _buf_cont_write(streambuf)); | |
86 | ||
87 | if (fd < 0 || !space || stream.state <= STREAMING_WAIT) { | |
88 | UNLOCK; | |
89 | usleep(100000); | |
90 | continue; | |
91 | } | |
92 | ||
93 | if (stream.state == STREAMING_FILE) { | |
94 | ||
95 | int n = read(fd, streambuf->writep, space); | |
96 | if (n == 0) { | |
97 | LOG_INFO("end of stream"); | |
98 | _disconnect(DISCONNECT, DISCONNECT_OK); | |
99 | } | |
100 | if (n > 0) { | |
101 | _buf_inc_writep(streambuf, n); | |
102 | stream.bytes += n; | |
103 | LOG_SDEBUG("streambuf read %d bytes", n); | |
104 | } | |
105 | if (n < 0) { | |
106 | LOG_WARN("error reading: %s", strerror(last_error())); | |
107 | _disconnect(DISCONNECT, REMOTE_DISCONNECT); | |
108 | } | |
109 | ||
110 | UNLOCK; | |
111 | continue; | |
112 | ||
113 | } else { | |
114 | ||
115 | pollinfo.fd = fd; | |
116 | pollinfo.events = POLLIN; | |
117 | if (stream.state == SEND_HEADERS) { | |
118 | pollinfo.events |= POLLOUT; | |
119 | } | |
120 | } | |
121 | ||
122 | UNLOCK; | |
123 | ||
124 | if (poll(&pollinfo, 1, 100)) { | |
125 | ||
126 | LOCK; | |
127 | ||
128 | // check socket has not been closed while in poll | |
129 | if (fd < 0) { | |
130 | UNLOCK; | |
131 | continue; | |
132 | } | |
133 | ||
134 | if ((pollinfo.revents & POLLOUT) && stream.state == SEND_HEADERS) { | |
135 | send_header(); | |
136 | stream.header_len = 0; | |
137 | stream.state = RECV_HEADERS; | |
138 | UNLOCK; | |
139 | continue; | |
140 | } | |
141 | ||
142 | if (pollinfo.revents & (POLLIN | POLLHUP)) { | |
143 | ||
144 | // get response headers | |
145 | if (stream.state == RECV_HEADERS) { | |
146 | ||
147 | // read one byte at a time to catch end of header | |
148 | char c; | |
149 | static int endtok; | |
150 | ||
151 | int n = recv(fd, &c, 1, 0); | |
152 | if (n <= 0) { | |
153 | if (n < 0 && last_error() == ERROR_WOULDBLOCK) { | |
154 | UNLOCK; | |
155 | continue; | |
156 | } | |
157 | LOG_INFO("error reading headers: %s", n ? strerror(last_error()) : "closed"); | |
158 | _disconnect(STOPPED, LOCAL_DISCONNECT); | |
159 | UNLOCK; | |
160 | continue; | |
161 | } | |
162 | ||
163 | *(stream.header + stream.header_len) = c; | |
164 | stream.header_len++; | |
165 | ||
166 | if (stream.header_len > MAX_HEADER - 1) { | |
167 | LOG_ERROR("received headers too long: %u", stream.header_len); | |
168 | _disconnect(DISCONNECT, LOCAL_DISCONNECT); | |
169 | } | |
170 | ||
171 | if (stream.header_len > 1 && (c == '\r' || c == '\n')) { | |
172 | endtok++; | |
173 | if (endtok == 4) { | |
174 | *(stream.header + stream.header_len) = '\0'; | |
175 | LOG_INFO("headers: len: %d\n%s", stream.header_len, stream.header); | |
176 | stream.state = stream.cont_wait ? STREAMING_WAIT : STREAMING_BUFFERING; | |
177 | wake_controller(); | |
178 | } | |
179 | } else { | |
180 | endtok = 0; | |
181 | } | |
182 | ||
183 | UNLOCK; | |
184 | continue; | |
185 | } | |
186 | ||
187 | // receive icy meta data | |
188 | if (stream.meta_interval && stream.meta_next == 0) { | |
189 | ||
190 | if (stream.meta_left == 0) { | |
191 | // read meta length | |
192 | u8_t c; | |
193 | int n = recv(fd, &c, 1, 0); | |
194 | if (n <= 0) { | |
195 | if (n < 0 && last_error() == ERROR_WOULDBLOCK) { | |
196 | UNLOCK; | |
197 | continue; | |
198 | } | |
199 | LOG_INFO("error reading icy meta: %s", n ? strerror(last_error()) : "closed"); | |
200 | _disconnect(STOPPED, LOCAL_DISCONNECT); | |
201 | UNLOCK; | |
202 | continue; | |
203 | } | |
204 | stream.meta_left = 16 * c; | |
205 | stream.header_len = 0; // amount of received meta data | |
206 | // MAX_HEADER must be more than meta max of 16 * 255 | |
207 | } | |
208 | ||
209 | if (stream.meta_left) { | |
210 | int n = recv(fd, stream.header + stream.header_len, stream.meta_left, 0); | |
211 | if (n <= 0) { | |
212 | if (n < 0 && last_error() == ERROR_WOULDBLOCK) { | |
213 | UNLOCK; | |
214 | continue; | |
215 | } | |
216 | LOG_INFO("error reading icy meta: %s", n ? strerror(last_error()) : "closed"); | |
217 | _disconnect(STOPPED, LOCAL_DISCONNECT); | |
218 | UNLOCK; | |
219 | continue; | |
220 | } | |
221 | stream.meta_left -= n; | |
222 | stream.header_len += n; | |
223 | } | |
224 | ||
225 | if (stream.meta_left == 0) { | |
226 | if (stream.header_len) { | |
227 | *(stream.header + stream.header_len) = '\0'; | |
228 | LOG_INFO("icy meta: len: %u\n%s", stream.header_len, stream.header); | |
229 | stream.meta_send = true; | |
230 | wake_controller(); | |
231 | } | |
232 | stream.meta_next = stream.meta_interval; | |
233 | UNLOCK; | |
234 | continue; | |
235 | } | |
236 | ||
237 | // stream body into streambuf | |
238 | } else { | |
239 | int n; | |
240 | ||
241 | space = min(_buf_space(streambuf), _buf_cont_write(streambuf)); | |
242 | if (stream.meta_interval) { | |
243 | space = min(space, stream.meta_next); | |
244 | } | |
245 | ||
246 | n = recv(fd, streambuf->writep, space, 0); | |
247 | if (n == 0) { | |
248 | LOG_INFO("end of stream"); | |
249 | _disconnect(DISCONNECT, DISCONNECT_OK); | |
250 | } | |
251 | if (n < 0 && last_error() != ERROR_WOULDBLOCK) { | |
252 | LOG_INFO("error reading: %s", strerror(last_error())); | |
253 | _disconnect(DISCONNECT, REMOTE_DISCONNECT); | |
254 | } | |
255 | ||
256 | if (n > 0) { | |
257 | _buf_inc_writep(streambuf, n); | |
258 | stream.bytes += n; | |
259 | if (stream.meta_interval) { | |
260 | stream.meta_next -= n; | |
261 | } | |
262 | } | |
263 | ||
264 | if (stream.state == STREAMING_BUFFERING && stream.bytes > stream.threshold) { | |
265 | stream.state = STREAMING_HTTP; | |
266 | wake_controller(); | |
267 | } | |
268 | ||
269 | LOG_SDEBUG("streambuf read %d bytes", n); | |
270 | } | |
271 | } | |
272 | ||
273 | UNLOCK; | |
274 | ||
275 | } else { | |
276 | ||
277 | LOG_SDEBUG("poll timeout"); | |
278 | } | |
279 | } | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
284 | static thread_type thread; | |
285 | ||
286 | void stream_init(log_level level, unsigned stream_buf_size) { | |
287 | loglevel = level; | |
288 | ||
289 | LOG_INFO("init stream"); | |
290 | LOG_DEBUG("streambuf size: %u", stream_buf_size); | |
291 | ||
292 | buf_init(streambuf, stream_buf_size); | |
293 | if (streambuf->buf == NULL) { | |
294 | LOG_ERROR("unable to malloc buffer"); | |
295 | exit(0); | |
296 | } | |
297 | ||
298 | stream.state = STOPPED; | |
299 | stream.header = malloc(MAX_HEADER); | |
300 | *stream.header = '\0'; | |
301 | ||
302 | fd = -1; | |
303 | ||
304 | #if LINUX || FREEBSD | |
305 | touch_memory(streambuf->buf, streambuf->size); | |
306 | #endif | |
307 | ||
308 | #if LINUX || OSX || FREEBSD | |
309 | pthread_attr_t attr; | |
310 | pthread_attr_init(&attr); | |
311 | pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + STREAM_THREAD_STACK_SIZE); | |
312 | pthread_create(&thread, &attr, stream_thread, NULL); | |
313 | pthread_attr_destroy(&attr); | |
314 | #endif | |
315 | #if WIN | |
316 | thread = CreateThread(NULL, STREAM_THREAD_STACK_SIZE, (LPTHREAD_START_ROUTINE)&stream_thread, NULL, 0, NULL); | |
317 | #endif | |
318 | } | |
319 | ||
320 | void stream_close(void) { | |
321 | LOG_INFO("close stream"); | |
322 | LOCK; | |
323 | running = false; | |
324 | UNLOCK; | |
325 | #if LINUX || OSX || FREEBSD | |
326 | pthread_join(thread, NULL); | |
327 | #endif | |
328 | free(stream.header); | |
329 | buf_destroy(streambuf); | |
330 | } | |
331 | ||
332 | void stream_file(const char *header, size_t header_len, unsigned threshold) { | |
333 | buf_flush(streambuf); | |
334 | ||
335 | LOCK; | |
336 | ||
337 | stream.header_len = header_len; | |
338 | memcpy(stream.header, header, header_len); | |
339 | *(stream.header+header_len) = '\0'; | |
340 | ||
341 | LOG_INFO("opening local file: %s", stream.header); | |
342 | ||
343 | #if WIN | |
344 | fd = open(stream.header, O_RDONLY | O_BINARY); | |
345 | #else | |
346 | fd = open(stream.header, O_RDONLY); | |
347 | #endif | |
348 | ||
349 | stream.state = STREAMING_FILE; | |
350 | if (fd < 0) { | |
351 | LOG_INFO("can't open file: %s", stream.header); | |
352 | stream.state = DISCONNECT; | |
353 | } | |
354 | wake_controller(); | |
355 | ||
356 | stream.cont_wait = false; | |
357 | stream.meta_interval = 0; | |
358 | stream.meta_next = 0; | |
359 | stream.meta_left = 0; | |
360 | stream.meta_send = false; | |
361 | stream.sent_headers = false; | |
362 | stream.bytes = 0; | |
363 | stream.threshold = threshold; | |
364 | ||
365 | UNLOCK; | |
366 | } | |
367 | ||
368 | void stream_sock(u32_t ip, u16_t port, const char *header, size_t header_len, unsigned threshold, bool cont_wait) { | |
369 | struct sockaddr_in addr; | |
370 | ||
371 | int sock = socket(AF_INET, SOCK_STREAM, 0); | |
372 | ||
373 | if (sock < 0) { | |
374 | LOG_ERROR("failed to create socket"); | |
375 | return; | |
376 | } | |
377 | ||
378 | memset(&addr, 0, sizeof(addr)); | |
379 | addr.sin_family = AF_INET; | |
380 | addr.sin_addr.s_addr = ip; | |
381 | addr.sin_port = port; | |
382 | ||
383 | LOG_INFO("connecting to %s:%d", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); | |
384 | ||
385 | set_nonblock(sock); | |
386 | set_nosigpipe(sock); | |
387 | ||
388 | if (connect_timeout(sock, (struct sockaddr *) &addr, sizeof(addr), 10) < 0) { | |
389 | LOG_INFO("unable to connect to server"); | |
390 | LOCK; | |
391 | stream.state = DISCONNECT; | |
392 | stream.disconnect = UNREACHABLE; | |
393 | UNLOCK; | |
394 | return; | |
395 | } | |
396 | ||
397 | buf_flush(streambuf); | |
398 | ||
399 | LOCK; | |
400 | ||
401 | fd = sock; | |
402 | stream.state = SEND_HEADERS; | |
403 | stream.cont_wait = cont_wait; | |
404 | stream.meta_interval = 0; | |
405 | stream.meta_next = 0; | |
406 | stream.meta_left = 0; | |
407 | stream.meta_send = false; | |
408 | stream.header_len = header_len; | |
409 | memcpy(stream.header, header, header_len); | |
410 | *(stream.header+header_len) = '\0'; | |
411 | ||
412 | LOG_INFO("header: %s", stream.header); | |
413 | ||
414 | stream.sent_headers = false; | |
415 | stream.bytes = 0; | |
416 | stream.threshold = threshold; | |
417 | ||
418 | UNLOCK; | |
419 | } | |
420 | ||
421 | bool stream_disconnect(void) { | |
422 | bool disc = false; | |
423 | LOCK; | |
424 | if (fd != -1) { | |
425 | closesocket(fd); | |
426 | fd = -1; | |
427 | disc = true; | |
428 | } | |
429 | stream.state = STOPPED; | |
430 | UNLOCK; | |
431 | return disc; | |
432 | } |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 "squeezelite.h" | |
21 | ||
22 | #if LINUX || OSX || FREEBSD | |
23 | #include <sys/ioctl.h> | |
24 | #include <net/if.h> | |
25 | #include <netdb.h> | |
26 | #if FREEBSD | |
27 | #include <ifaddrs.h> | |
28 | #include <net/if_dl.h> | |
29 | #include <net/if_types.h> | |
30 | #endif | |
31 | #endif | |
32 | #if WIN | |
33 | #include <iphlpapi.h> | |
34 | #endif | |
35 | #if OSX | |
36 | #include <net/if_dl.h> | |
37 | #include <net/if_types.h> | |
38 | #include <ifaddrs.h> | |
39 | #include <netdb.h> | |
40 | #endif | |
41 | ||
42 | #include <fcntl.h> | |
43 | ||
44 | // logging functions | |
45 | const char *logtime(void) { | |
46 | static char buf[100]; | |
47 | #if WIN | |
48 | SYSTEMTIME lt; | |
49 | GetLocalTime(<); | |
50 | sprintf(buf, "[%02d:%02d:%02d.%03d]", lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds); | |
51 | #else | |
52 | struct timeval tv; | |
53 | gettimeofday(&tv, NULL); | |
54 | strftime(buf, sizeof(buf), "[%T.", localtime(&tv.tv_sec)); | |
55 | sprintf(buf+strlen(buf), "%06ld]", (long)tv.tv_usec); | |
56 | #endif | |
57 | return buf; | |
58 | } | |
59 | ||
60 | void logprint(const char *fmt, ...) { | |
61 | va_list args; | |
62 | va_start(args, fmt); | |
63 | vfprintf(stderr, fmt, args); | |
64 | fflush(stderr); | |
65 | } | |
66 | ||
67 | // cmdline parsing | |
68 | char *next_param(char *src, char c) { | |
69 | static char *str = NULL; | |
70 | char *ptr, *ret; | |
71 | if (src) str = src; | |
72 | if (str && (ptr = strchr(str, c))) { | |
73 | ret = str; | |
74 | *ptr = '\0'; | |
75 | str = ptr + 1; | |
76 | } else { | |
77 | ret = str; | |
78 | str = NULL; | |
79 | } | |
80 | ||
81 | return ret && ret[0] ? ret : NULL; | |
82 | } | |
83 | ||
84 | // clock | |
85 | u32_t gettime_ms(void) { | |
86 | #if WIN | |
87 | return GetTickCount(); | |
88 | #else | |
89 | #if LINUX || FREEBSD | |
90 | struct timespec ts; | |
91 | if (!clock_gettime(CLOCK_MONOTONIC, &ts)) { | |
92 | return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; | |
93 | } | |
94 | #endif | |
95 | struct timeval tv; | |
96 | gettimeofday(&tv, NULL); | |
97 | return tv.tv_sec * 1000 + tv.tv_usec / 1000; | |
98 | #endif | |
99 | } | |
100 | ||
101 | // mac address | |
102 | #if LINUX | |
103 | // search first 4 interfaces returned by IFCONF | |
104 | void get_mac(u8_t mac[]) { | |
105 | struct ifconf ifc; | |
106 | struct ifreq *ifr, *ifend; | |
107 | struct ifreq ifreq; | |
108 | struct ifreq ifs[4]; | |
109 | ||
110 | mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0; | |
111 | ||
112 | int s = socket(AF_INET, SOCK_DGRAM, 0); | |
113 | ||
114 | ifc.ifc_len = sizeof(ifs); | |
115 | ifc.ifc_req = ifs; | |
116 | ||
117 | if (ioctl(s, SIOCGIFCONF, &ifc) == 0) { | |
118 | ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq)); | |
119 | ||
120 | for (ifr = ifc.ifc_req; ifr < ifend; ifr++) { | |
121 | if (ifr->ifr_addr.sa_family == AF_INET) { | |
122 | ||
123 | strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name)); | |
124 | if (ioctl (s, SIOCGIFHWADDR, &ifreq) == 0) { | |
125 | memcpy(mac, ifreq.ifr_hwaddr.sa_data, 6); | |
126 | if (mac[0]+mac[1]+mac[2] != 0) { | |
127 | break; | |
128 | } | |
129 | } | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
134 | close(s); | |
135 | } | |
136 | #endif | |
137 | ||
138 | #if OSX || FREEBSD | |
139 | void get_mac(u8_t mac[]) { | |
140 | struct ifaddrs *addrs, *ptr; | |
141 | const struct sockaddr_dl *dlAddr; | |
142 | const unsigned char *base; | |
143 | ||
144 | mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0; | |
145 | ||
146 | if (getifaddrs(&addrs) == 0) { | |
147 | ptr = addrs; | |
148 | while (ptr) { | |
149 | if (ptr->ifa_addr->sa_family == AF_LINK && ((const struct sockaddr_dl *) ptr->ifa_addr)->sdl_type == IFT_ETHER) { | |
150 | dlAddr = (const struct sockaddr_dl *)ptr->ifa_addr; | |
151 | base = (const unsigned char*) &dlAddr->sdl_data[dlAddr->sdl_nlen]; | |
152 | memcpy(mac, base, min(dlAddr->sdl_alen, 6)); | |
153 | break; | |
154 | } | |
155 | ptr = ptr->ifa_next; | |
156 | } | |
157 | freeifaddrs(addrs); | |
158 | } | |
159 | } | |
160 | #endif | |
161 | ||
162 | #if WIN | |
163 | #pragma comment(lib, "IPHLPAPI.lib") | |
164 | void get_mac(u8_t mac[]) { | |
165 | IP_ADAPTER_INFO AdapterInfo[16]; | |
166 | DWORD dwBufLen = sizeof(AdapterInfo); | |
167 | DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen); | |
168 | ||
169 | mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0; | |
170 | ||
171 | if (GetAdaptersInfo(AdapterInfo, &dwBufLen) == ERROR_SUCCESS) { | |
172 | memcpy(mac, AdapterInfo[0].Address, 6); | |
173 | } | |
174 | } | |
175 | #endif | |
176 | ||
177 | void set_nonblock(sockfd s) { | |
178 | #if WIN | |
179 | u_long iMode = 1; | |
180 | ioctlsocket(s, FIONBIO, &iMode); | |
181 | #else | |
182 | int flags = fcntl(s, F_GETFL,0); | |
183 | fcntl(s, F_SETFL, flags | O_NONBLOCK); | |
184 | #endif | |
185 | } | |
186 | ||
187 | // connect for socket already set to non blocking with timeout in seconds | |
188 | int connect_timeout(sockfd sock, const struct sockaddr *addr, socklen_t addrlen, int timeout) { | |
189 | fd_set w, e; | |
190 | struct timeval tval; | |
191 | ||
192 | if (connect(sock, addr, addrlen) < 0) { | |
193 | #if !WIN | |
194 | if (last_error() != EINPROGRESS) { | |
195 | #else | |
196 | if (last_error() != WSAEWOULDBLOCK) { | |
197 | #endif | |
198 | return -1; | |
199 | } | |
200 | } | |
201 | ||
202 | FD_ZERO(&w); | |
203 | FD_SET(sock, &w); | |
204 | e = w; | |
205 | tval.tv_sec = timeout; | |
206 | tval.tv_usec = 0; | |
207 | ||
208 | // only return 0 if w set and sock error is zero, otherwise return error code | |
209 | if (select(sock + 1, NULL, &w, &e, timeout ? &tval : NULL) == 1 && FD_ISSET(sock, &w)) { | |
210 | int error = 0; | |
211 | socklen_t len = sizeof(error); | |
212 | getsockopt(sock, SOL_SOCKET, SO_ERROR, (void *)&error, &len); | |
213 | return error; | |
214 | } | |
215 | ||
216 | return -1; | |
217 | } | |
218 | ||
219 | void server_addr(char *server, in_addr_t *ip_ptr, unsigned *port_ptr) { | |
220 | struct addrinfo *res = NULL; | |
221 | struct addrinfo hints; | |
222 | const char *port = NULL; | |
223 | ||
224 | if (strtok(server, ":")) { | |
225 | port = strtok(NULL, ":"); | |
226 | if (port) { | |
227 | *port_ptr = atoi(port); | |
228 | } | |
229 | } | |
230 | ||
231 | memset(&hints, 0, sizeof(struct addrinfo)); | |
232 | hints.ai_family = AF_INET; | |
233 | ||
234 | getaddrinfo(server, NULL, &hints, &res); | |
235 | ||
236 | if (res && res->ai_addr) { | |
237 | *ip_ptr = ((struct sockaddr_in*)res->ai_addr)->sin_addr.s_addr; | |
238 | } | |
239 | ||
240 | if (res) { | |
241 | freeaddrinfo(res); | |
242 | } | |
243 | } | |
244 | ||
245 | void set_readwake_handles(event_handle handles[], sockfd s, event_event e) { | |
246 | #if WINEVENT | |
247 | handles[0] = WSACreateEvent(); | |
248 | handles[1] = e; | |
249 | WSAEventSelect(s, handles[0], FD_READ | FD_CLOSE); | |
250 | #elif SELFPIPE | |
251 | handles[0].fd = s; | |
252 | handles[1].fd = e.fds[0]; | |
253 | handles[0].events = POLLIN; | |
254 | handles[1].events = POLLIN; | |
255 | #else | |
256 | handles[0].fd = s; | |
257 | handles[1].fd = e; | |
258 | handles[0].events = POLLIN; | |
259 | handles[1].events = POLLIN; | |
260 | #endif | |
261 | } | |
262 | ||
263 | event_type wait_readwake(event_handle handles[], int timeout) { | |
264 | #if WINEVENT | |
265 | int wait = WSAWaitForMultipleEvents(2, handles, FALSE, timeout, FALSE); | |
266 | if (wait == WSA_WAIT_EVENT_0) { | |
267 | WSAResetEvent(handles[0]); | |
268 | return EVENT_READ; | |
269 | } else if (wait == WSA_WAIT_EVENT_0 + 1) { | |
270 | return EVENT_WAKE; | |
271 | } else { | |
272 | return EVENT_TIMEOUT; | |
273 | } | |
274 | #else | |
275 | if (poll(handles, 2, timeout) > 0) { | |
276 | if (handles[0].revents) { | |
277 | return EVENT_READ; | |
278 | } | |
279 | if (handles[1].revents) { | |
280 | wake_clear(handles[1].fd); | |
281 | return EVENT_WAKE; | |
282 | } | |
283 | } | |
284 | return EVENT_TIMEOUT; | |
285 | #endif | |
286 | } | |
287 | ||
288 | // pack/unpack to network byte order | |
289 | void packN(u32_t *dest, u32_t val) { | |
290 | u8_t *ptr = (u8_t *)dest; | |
291 | *(ptr) = (val >> 24) & 0xFF; *(ptr+1) = (val >> 16) & 0xFF; *(ptr+2) = (val >> 8) & 0xFF; *(ptr+3) = val & 0xFF; | |
292 | } | |
293 | ||
294 | void packn(u16_t *dest, u16_t val) { | |
295 | u8_t *ptr = (u8_t *)dest; | |
296 | *(ptr) = (val >> 8) & 0xFF; *(ptr+1) = val & 0xFF; | |
297 | } | |
298 | ||
299 | u32_t unpackN(u32_t *src) { | |
300 | u8_t *ptr = (u8_t *)src; | |
301 | return *(ptr) << 24 | *(ptr+1) << 16 | *(ptr+2) << 8 | *(ptr+3); | |
302 | } | |
303 | ||
304 | u16_t unpackn(u16_t *src) { | |
305 | u8_t *ptr = (u8_t *)src; | |
306 | return *(ptr) << 8 | *(ptr+1); | |
307 | } | |
308 | ||
309 | #if OSX | |
310 | void set_nosigpipe(sockfd s) { | |
311 | int set = 1; | |
312 | setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); | |
313 | } | |
314 | #endif | |
315 | ||
316 | #if WIN | |
317 | void winsock_init(void) { | |
318 | WSADATA wsaData; | |
319 | WORD wVersionRequested = MAKEWORD(2, 2); | |
320 | int WSerr = WSAStartup(wVersionRequested, &wsaData); | |
321 | if (WSerr != 0) { | |
322 | LOG_ERROR("Bad winsock version"); | |
323 | exit(1); | |
324 | } | |
325 | } | |
326 | ||
327 | void winsock_close(void) { | |
328 | WSACleanup(); | |
329 | } | |
330 | ||
331 | void *dlopen(const char *filename, int flag) { | |
332 | SetLastError(0); | |
333 | return LoadLibrary((LPCTSTR)filename); | |
334 | } | |
335 | ||
336 | void *dlsym(void *handle, const char *symbol) { | |
337 | SetLastError(0); | |
338 | return (void *)GetProcAddress(handle, symbol); | |
339 | } | |
340 | ||
341 | char *dlerror(void) { | |
342 | static char ret[32]; | |
343 | int last = GetLastError(); | |
344 | if (last) { | |
345 | sprintf(ret, "code: %i", last); | |
346 | SetLastError(0); | |
347 | return ret; | |
348 | } | |
349 | return NULL; | |
350 | } | |
351 | ||
352 | // this only implements numfds == 1 | |
353 | int poll(struct pollfd *fds, unsigned long numfds, int timeout) { | |
354 | fd_set r, w; | |
355 | struct timeval tv; | |
356 | int ret; | |
357 | ||
358 | FD_ZERO(&r); | |
359 | FD_ZERO(&w); | |
360 | ||
361 | if (fds[0].events & POLLIN) FD_SET(fds[0].fd, &r); | |
362 | if (fds[0].events & POLLOUT) FD_SET(fds[0].fd, &w); | |
363 | ||
364 | tv.tv_sec = timeout / 1000; | |
365 | tv.tv_usec = 1000 * (timeout % 1000); | |
366 | ||
367 | ret = select(fds[0].fd + 1, &r, &w, NULL, &tv); | |
368 | ||
369 | if (ret < 0) return ret; | |
370 | ||
371 | fds[0].revents = 0; | |
372 | if (FD_ISSET(fds[0].fd, &r)) fds[0].revents |= POLLIN; | |
373 | if (FD_ISSET(fds[0].fd, &w)) fds[0].revents |= POLLOUT; | |
374 | ||
375 | return ret; | |
376 | } | |
377 | ||
378 | #endif | |
379 | ||
380 | #if LINUX || FREEBSD | |
381 | void touch_memory(u8_t *buf, size_t size) { | |
382 | u8_t *ptr; | |
383 | for (ptr = buf; ptr < buf + size; ptr += sysconf(_SC_PAGESIZE)) { | |
384 | *ptr = 0; | |
385 | } | |
386 | } | |
387 | #endif |
0 | /* | |
1 | * Squeezelite - lightweight headless squeezebox emulator | |
2 | * | |
3 | * (c) Adrian Smith 2012-2015, triode1@btinternet.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 "squeezelite.h" | |
21 | ||
22 | // automatically select between floating point (preferred) and fixed point libraries: | |
23 | // NOTE: works with Tremor version here: http://svn.xiph.org/trunk/Tremor, not vorbisidec.1.0.2 currently in ubuntu | |
24 | ||
25 | // we take common definations from <vorbis/vorbisfile.h> even though we can use tremor at run time | |
26 | // tremor's OggVorbis_File struct is normally smaller so this is ok, but padding added to malloc in case it is bigger | |
27 | #define OV_EXCLUDE_STATIC_CALLBACKS | |
28 | ||
29 | #include <vorbis/vorbisfile.h> | |
30 | ||
31 | struct vorbis { | |
32 | OggVorbis_File *vf; | |
33 | bool opened; | |
34 | #if !LINKALL | |
35 | // vorbis symbols to be dynamically loaded - from either vorbisfile or vorbisidec (tremor) version of library | |
36 | vorbis_info *(* ov_info)(OggVorbis_File *vf, int link); | |
37 | int (* ov_clear)(OggVorbis_File *vf); | |
38 | long (* ov_read)(OggVorbis_File *vf, char *buffer, int length, int bigendianp, int word, int sgned, int *bitstream); | |
39 | long (* ov_read_tremor)(OggVorbis_File *vf, char *buffer, int length, int *bitstream); | |
40 | int (* ov_open_callbacks)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks); | |
41 | #endif | |
42 | }; | |
43 | ||
44 | static struct vorbis *v; | |
45 | ||
46 | extern log_level loglevel; | |
47 | ||
48 | extern struct buffer *streambuf; | |
49 | extern struct buffer *outputbuf; | |
50 | extern struct streamstate stream; | |
51 | extern struct outputstate output; | |
52 | extern struct decodestate decode; | |
53 | extern struct processstate process; | |
54 | ||
55 | #define LOCK_S mutex_lock(streambuf->mutex) | |
56 | #define UNLOCK_S mutex_unlock(streambuf->mutex) | |
57 | #define LOCK_O mutex_lock(outputbuf->mutex) | |
58 | #define UNLOCK_O mutex_unlock(outputbuf->mutex) | |
59 | #if PROCESS | |
60 | #define LOCK_O_direct if (decode.direct) mutex_lock(outputbuf->mutex) | |
61 | #define UNLOCK_O_direct if (decode.direct) mutex_unlock(outputbuf->mutex) | |
62 | #define LOCK_O_not_direct if (!decode.direct) mutex_lock(outputbuf->mutex) | |
63 | #define UNLOCK_O_not_direct if (!decode.direct) mutex_unlock(outputbuf->mutex) | |
64 | #define IF_DIRECT(x) if (decode.direct) { x } | |
65 | #define IF_PROCESS(x) if (!decode.direct) { x } | |
66 | #else | |
67 | #define LOCK_O_direct mutex_lock(outputbuf->mutex) | |
68 | #define UNLOCK_O_direct mutex_unlock(outputbuf->mutex) | |
69 | #define LOCK_O_not_direct | |
70 | #define UNLOCK_O_not_direct | |
71 | #define IF_DIRECT(x) { x } | |
72 | #define IF_PROCESS(x) | |
73 | #endif | |
74 | ||
75 | #if LINKALL | |
76 | #define OV(h, fn, ...) (ov_ ## fn)(__VA_ARGS__) | |
77 | #define TREMOR(h) 0 | |
78 | extern int ov_read_tremor(); // needed to enable compilation, not linked | |
79 | #else | |
80 | #define OV(h, fn, ...) (h)->ov_##fn(__VA_ARGS__) | |
81 | #define TREMOR(h) (h)->ov_read_tremor | |
82 | #endif | |
83 | ||
84 | // called with mutex locked within vorbis_decode to avoid locking O before S | |
85 | static size_t _read_cb(void *ptr, size_t size, size_t nmemb, void *datasource) { | |
86 | size_t bytes; | |
87 | ||
88 | bytes = min(_buf_used(streambuf), _buf_cont_read(streambuf)); | |
89 | bytes = min(bytes, size * nmemb); | |
90 | ||
91 | memcpy(ptr, streambuf->readp, bytes); | |
92 | _buf_inc_readp(streambuf, bytes); | |
93 | ||
94 | return bytes / size; | |
95 | } | |
96 | ||
97 | // these are needed for older versions of tremor, later versions and libvorbis allow NULL to be used | |
98 | static int _seek_cb(void *datasource, ogg_int64_t offset, int whence) { return -1; } | |
99 | static int _close_cb(void *datasource) { return 0; } | |
100 | static long _tell_cb(void *datasource) { return 0; } | |
101 | ||
102 | static decode_state vorbis_decode(void) { | |
103 | static int channels; | |
104 | bool end; | |
105 | frames_t frames; | |
106 | int bytes, s, n; | |
107 | u8_t *write_buf; | |
108 | ||
109 | LOCK_S; | |
110 | LOCK_O_direct; | |
111 | end = (stream.state <= DISCONNECT); | |
112 | ||
113 | IF_DIRECT( | |
114 | frames = min(_buf_space(outputbuf), _buf_cont_write(outputbuf)) / BYTES_PER_FRAME; | |
115 | ); | |
116 | IF_PROCESS( | |
117 | frames = process.max_in_frames; | |
118 | ); | |
119 | ||
120 | if (!frames && end) { | |
121 | UNLOCK_O_direct; | |
122 | UNLOCK_S; | |
123 | return DECODE_COMPLETE; | |
124 | } | |
125 | ||
126 | if (decode.new_stream) { | |
127 | ov_callbacks cbs; | |
128 | int err; | |
129 | struct vorbis_info *info; | |
130 | ||
131 | cbs.read_func = _read_cb; | |
132 | ||
133 | if (TREMOR(v)) { | |
134 | cbs.seek_func = _seek_cb; cbs.close_func = _close_cb; cbs.tell_func = _tell_cb; | |
135 | } else { | |
136 | cbs.seek_func = NULL; cbs.close_func = NULL; cbs.tell_func = NULL; | |
137 | } | |
138 | ||
139 | if ((err = OV(v, open_callbacks, streambuf, v->vf, NULL, 0, cbs)) < 0) { | |
140 | LOG_WARN("open_callbacks error: %d", err); | |
141 | UNLOCK_O_direct; | |
142 | UNLOCK_S; | |
143 | return DECODE_COMPLETE; | |
144 | } | |
145 | v->opened = true; | |
146 | ||
147 | info = OV(v, info, v->vf, -1); | |
148 | ||
149 | LOG_INFO("setting track_start"); | |
150 | LOCK_O_not_direct; | |
151 | output.next_sample_rate = decode_newstream(info->rate, output.supported_rates); | |
152 | IF_DSD( output.next_dop = false; ) | |
153 | output.track_start = outputbuf->writep; | |
154 | if (output.fade_mode) _checkfade(true); | |
155 | decode.new_stream = false; | |
156 | UNLOCK_O_not_direct; | |
157 | ||
158 | IF_PROCESS( | |
159 | frames = process.max_in_frames; | |
160 | ); | |
161 | ||
162 | channels = info->channels; | |
163 | ||
164 | if (channels > 2) { | |
165 | LOG_WARN("too many channels: %d", channels); | |
166 | UNLOCK_O_direct; | |
167 | UNLOCK_S; | |
168 | return DECODE_ERROR; | |
169 | } | |
170 | } | |
171 | ||
172 | bytes = frames * 2 * channels; // samples returned are 16 bits | |
173 | ||
174 | IF_DIRECT( | |
175 | write_buf = outputbuf->writep; | |
176 | ); | |
177 | IF_PROCESS( | |
178 | write_buf = process.inbuf; | |
179 | ); | |
180 | ||
181 | // write the decoded frames into outputbuf even though they are 16 bits per sample, then unpack them | |
182 | if (!TREMOR(v)) { | |
183 | #if SL_LITTLE_ENDIAN | |
184 | n = OV(v, read, v->vf, (char *)write_buf, bytes, 0, 2, 1, &s); | |
185 | #else | |
186 | n = OV(v, read, v->vf, (char *)write_buf, bytes, 1, 2, 1, &s); | |
187 | #endif | |
188 | } else { | |
189 | n = OV(v, read_tremor, v->vf, (char *)write_buf, bytes, &s); | |
190 | } | |
191 | ||
192 | if (n > 0) { | |
193 | ||
194 | frames_t count; | |
195 | s16_t *iptr; | |
196 | s32_t *optr; | |
197 | ||
198 | frames = n / 2 / channels; | |
199 | count = frames * channels; | |
200 | ||
201 | // work backward to unpack samples to 4 bytes per sample | |
202 | iptr = (s16_t *)write_buf + count; | |
203 | optr = (s32_t *)write_buf + frames * 2; | |
204 | ||
205 | if (channels == 2) { | |
206 | while (count--) { | |
207 | *--optr = *--iptr << 16; | |
208 | } | |
209 | } else if (channels == 1) { | |
210 | while (count--) { | |
211 | *--optr = *--iptr << 16; | |
212 | *--optr = *iptr << 16; | |
213 | } | |
214 | } | |
215 | ||
216 | IF_DIRECT( | |
217 | _buf_inc_writep(outputbuf, frames * BYTES_PER_FRAME); | |
218 | ); | |
219 | IF_PROCESS( | |
220 | process.in_frames = frames; | |
221 | ); | |
222 | ||
223 | LOG_SDEBUG("wrote %u frames", frames); | |
224 | ||
225 | } else if (n == 0) { | |
226 | ||
227 | LOG_INFO("end of stream"); | |
228 | UNLOCK_O_direct; | |
229 | UNLOCK_S; | |
230 | return DECODE_COMPLETE; | |
231 | ||
232 | } else if (n == OV_HOLE) { | |
233 | ||
234 | // recoverable hole in stream, seen when skipping | |
235 | LOG_DEBUG("hole in stream"); | |
236 | ||
237 | } else { | |
238 | ||
239 | LOG_INFO("ov_read error: %d", n); | |
240 | UNLOCK_O_direct; | |
241 | UNLOCK_S; | |
242 | return DECODE_COMPLETE; | |
243 | } | |
244 | ||
245 | UNLOCK_O_direct; | |
246 | UNLOCK_S; | |
247 | ||
248 | return DECODE_RUNNING; | |
249 | } | |
250 | ||
251 | static void vorbis_open(u8_t size, u8_t rate, u8_t chan, u8_t endianness) { | |
252 | if (!v->vf) { | |
253 | v->vf = malloc(sizeof(OggVorbis_File) + 128); // add some padding as struct size may be larger | |
254 | memset(v->vf, 0, sizeof(OggVorbis_File) + 128); | |
255 | } else { | |
256 | if (v->opened) { | |
257 | OV(v, clear, v->vf); | |
258 | v->opened = false; | |
259 | } | |
260 | } | |
261 | } | |
262 | ||
263 | static void vorbis_close(void) { | |
264 | if (v->opened) { | |
265 | OV(v, clear, v->vf); | |
266 | v->opened = false; | |
267 | } | |
268 | free(v->vf); | |
269 | v->vf = NULL; | |
270 | } | |
271 | ||
272 | static bool load_vorbis() { | |
273 | #if !LINKALL | |
274 | void *handle = dlopen(LIBVORBIS, RTLD_NOW); | |
275 | char *err; | |
276 | bool tremor = false; | |
277 | ||
278 | if (!handle) { | |
279 | handle = dlopen(LIBTREMOR, RTLD_NOW); | |
280 | if (handle) { | |
281 | tremor = true; | |
282 | } else { | |
283 | LOG_INFO("dlerror: %s", dlerror()); | |
284 | return false; | |
285 | } | |
286 | } | |
287 | ||
288 | v->ov_read = tremor ? NULL : dlsym(handle, "ov_read"); | |
289 | v->ov_read_tremor = tremor ? dlsym(handle, "ov_read") : NULL; | |
290 | v->ov_info = dlsym(handle, "ov_info"); | |
291 | v->ov_clear = dlsym(handle, "ov_clear"); | |
292 | v->ov_open_callbacks = dlsym(handle, "ov_open_callbacks"); | |
293 | ||
294 | if ((err = dlerror()) != NULL) { | |
295 | LOG_INFO("dlerror: %s", err); | |
296 | return false; | |
297 | } | |
298 | ||
299 | LOG_INFO("loaded %s", tremor ? LIBTREMOR : LIBVORBIS); | |
300 | #endif | |
301 | ||
302 | return true; | |
303 | } | |
304 | ||
305 | struct codec *register_vorbis(void) { | |
306 | static struct codec ret = { | |
307 | 'o', // id | |
308 | "ogg", // types | |
309 | 2048, // min read | |
310 | 20480, // min space | |
311 | vorbis_open, // open | |
312 | vorbis_close, // close | |
313 | vorbis_decode,// decode | |
314 | }; | |
315 | ||
316 | v = malloc(sizeof(struct vorbis)); | |
317 | if (!v) { | |
318 | return NULL; | |
319 | } | |
320 | ||
321 | v->vf = NULL; | |
322 | v->opened = false; | |
323 | ||
324 | if (!load_vorbis()) { | |
325 | return NULL; | |
326 | } | |
327 | ||
328 | LOG_INFO("using vorbis to decode ogg"); | |
329 | return &ret; | |
330 | } |