Codebase list squeezelite / 4ded372
Merge tag 'upstream/1.8' into develop Tag upstream version 1.8 Chris Boot 8 years ago
28 changed file(s) with 959 addition(s) and 184 deletion(s). Raw diff Collapse all Expand all
102102
103103 Minor changes
104104 - 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)
00 Squeezelite - lightweight headless squeezebox emulator
11
2 (c) Adrian Smith 2012-2014, triode1@btinternet.com
2 (c) Adrian Smith 2012-2015, triode1@btinternet.com
33
44 Released under GPLv3 license:
55
1616 You should have received a copy of the GNU General Public License
1717 along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
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.
88 OPT_LINKALL = -DLINKALL
99 OPT_RESAMPLE= -DRESAMPLE
1010 OPT_VIS = -DVISEXPORT
11 OPT_IR = -DIR
1112
1213 SOURCES = \
1314 main.c slimproto.c buffer.c stream.c utils.c \
1819 SOURCES_FF = ffmpeg.c
1920 SOURCES_RESAMPLE = process.c resample.c
2021 SOURCES_VIS = output_vis.c
22 SOURCES_IR = ir.c
2123
2224 LINK_LINUX = -ldl
2325
2426 LINKALL = -lFLAC -lmad -lvorbisfile -lfaad -lmpg123
2527 LINKALL_FF = -lavcodec -lavformat -lavutil
2628 LINKALL_RESAMPLE = -lsoxr
29 LINKALL_IR = -llirc_client
2730
2831 DEPS = squeezelite.h slimproto.h
2932
4245 ifneq (,$(findstring $(OPT_VIS), $(CFLAGS)))
4346 SOURCES += $(SOURCES_VIS)
4447 endif
48 ifneq (,$(findstring $(OPT_IR), $(CFLAGS)))
49 SOURCES += $(SOURCES_IR)
50 endif
4551
4652 # add optional link options
4753 ifneq (,$(findstring $(OPT_LINKALL), $(CFLAGS)))
5157 endif
5258 ifneq (,$(findstring $(OPT_RESAMPLE), $(CFLAGS)))
5359 LDFLAGS += $(LINKALL_RESAMPLE)
60 endif
61 ifneq (,$(findstring $(OPT_IR), $(CFLAGS)))
62 LDFLAGS += $(LINKALL_IR)
5463 endif
5564 else
5665 # if not LINKALL and linux add LINK_LINUX
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
239239
240240 codec->open(sample_size, sample_rate, channels, endianness);
241241
242 decode.state = DECODE_READY;
243
242244 UNLOCK_D;
243245 return;
244246 }
+21
-10
dop.c less more
3636 u32_t next = 0;
3737
3838 while (frames--) {
39 if (((*lptr & 0x00FF0000) == 0x00050000 && (*rptr & 0x00FF0000) == 0x00050000) ||
39 if (((*lptr & 0x00FF0000) == 0x00050000 && (*rptr & 0x00FF0000) == 0x00050000) ||
4040 ((*lptr & 0x00FF0000) == 0x00FA0000 && (*rptr & 0x00FF0000) == 0x00FA0000)) {
4141 if (*lptr >> 24 == next) {
4242 matched++;
5757 return false;
5858 }
5959
60 // update the dop marker for frames in the output buffer
60 // update the dop marker and potentially invert polarity for frames in the output buffer
6161 // performaned on all output including silence to maintain marker phase consitency
62 void update_dop_marker(u32_t *ptr, frames_t frames) {
62 void update_dop(u32_t *ptr, frames_t frames, bool invert) {
6363 static u32_t marker = 0x05;
64 while (frames--) {
65 u32_t scaled_marker = marker << 24;
66 *ptr = (*ptr & 0x00FFFFFF) | scaled_marker;
67 ++ptr;
68 *ptr = (*ptr & 0x00FFFFFF) | scaled_marker;
69 ++ptr;
70 marker = ( 0x05 + 0xFA ) - marker;
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 }
7182 }
7283 }
7384
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
227227
228228 // found media data, advance to start of first chunk and return
229229 if (!strcmp(type, "mdat")) {
230 _buf_inc_readp(streambuf, 8);
230 _buf_inc_readp(streambuf, 8);
231231 a->pos += 8;
232232 bytes -= 8;
233233 if (play) {
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
609609 ff->avcodec_alloc_frame = dlsym(handle_codec, "avcodec_alloc_frame");
610610 ff->avcodec_free_frame = dlsym(handle_codec, "avcodec_free_frame");
611611 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");
612 ff->av_init_packet = dlsym(handle_codec, "av_init_packet");
613 ff->av_free_packet = dlsym(handle_codec, "av_free_packet");
614614
615615 if ((err = dlerror()) != NULL) {
616616 LOG_INFO("dlerror: %s", err);
619619
620620 LOG_INFO("loaded "LIBAVCODEC" (%u.%u.%u)", LIBAVCODEC_VERSION_MAJOR, ff->avcodec_version() >> 16, (ff->avcodec_version() >> 8) & 0xff, ff->avcodec_version() & 0xff);
621621
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");
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");
630630 ff->av_register_all = dlsym(handle_format, "av_register_all");
631631
632632 if ((err = dlerror()) != NULL) {
+248
-0
ir.c less more
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
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
+160
-31
main.c less more
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2121
2222 #include <signal.h>
2323
24 #define TITLE "Squeezelite " VERSION ", Copyright 2012-2014 Adrian Smith."
24 #define TITLE "Squeezelite " VERSION ", Copyright 2012-2015 Adrian Smith."
2525
2626 #define CODECS_BASE "flac,pcm,mp3,ogg,aac"
2727 #if FFMPEG
5757 " -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"
5858 " -b <stream>:<output>\tSpecify internal Stream and Output buffer sizes in Kbytes\n"
5959 " -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
6062 " -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
6166 " -e <codec1>,<codec2>\tExplicitly exclude native support of one or more codecs; known codecs: " CODECS "\n"
6267 " -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
6371 " -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"
6473 " -n <name>\t\tSet the player name\n"
6574 " -N <filename>\t\tStore player name in filename to allow server defined name changes to be shared between servers (not supported with -n)\n"
6675 #if ALSA
6776 " -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"
6880 #endif
6981 " -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"
7082 #if RESAMPLE
8395 #if VISEXPORT
8496 " -v \t\t\tVisualiser support\n"
8597 #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
86103 #if LINUX || FREEBSD
87104 " -z \t\t\tDaemonize\n"
88105 #endif
89106 " -t \t\t\tLicense terms\n"
107 " -? \t\t\tDisplay this help text\n"
90108 "\n"
91109 "Build options:"
92110 #if LINUX
128146 #endif
129147 #if VISEXPORT
130148 " VISEXPORT"
149 #endif
150 #if IR
151 " IR"
131152 #endif
132153 #if DSD
133154 " DSD"
172193 char *exclude_codecs = "";
173194 char *name = NULL;
174195 char *namefile = NULL;
196 char *modelname = NULL;
175197 char *logfile = NULL;
176198 u8_t mac[6];
177199 unsigned stream_buf_size = STREAMBUF_SIZE;
180202 unsigned rate_delay = 0;
181203 char *resample = NULL;
182204 char *output_params = NULL;
205 unsigned idle = 0;
183206 #if LINUX || FREEBSD
184207 bool daemonize = false;
208 char *pidfile = NULL;
209 FILE *pidfp = NULL;
185210 #endif
186211 #if ALSA
187212 unsigned rt_priority = OUTPUT_RT_PRIORITY;
213 char *output_mixer = NULL;
214 bool output_mixer_unmute = false;
188215 #endif
189216 #if DSD
190217 bool dop = false;
192219 #endif
193220 #if VISEXPORT
194221 bool visexport = false;
222 #endif
223 #if IR
224 char *lircrc = NULL;
195225 #endif
196226
197227 log_level log_output = lWARN;
198228 log_level log_stream = lWARN;
199229 log_level log_decode = lWARN;
200230 log_level log_slimproto = lWARN;
231 #if IR
232 log_level log_ir = lWARN;
233 #endif
201234
202235 char *optarg = NULL;
203236 int optind = 1;
204237 int i;
205238
206 #define MAXCMDLINE 256
239 #define MAXCMDLINE 512
207240 char cmdline[MAXCMDLINE] = "";
208241
209242 get_mac(mac);
215248
216249 while (optind < argc && strlen(argv[optind]) >= 2 && argv[optind][0] == '-') {
217250 char *opt = argv[optind] + 1;
218 if (strstr("oabcdefmnNprs", opt) && optind < argc - 1) {
251 if (strstr("oabcCdefmMnNpPrs"
252 #if ALSA
253 "UV"
254 #endif
255 , opt) && optind < argc - 1) {
219256 optarg = argv[optind + 1];
220257 optind += 2;
221 } else if (strstr("ltz"
258 } else if (strstr("ltz?"
259 #if ALSA
260 "L"
261 #endif
222262 #if RESAMPLE
223263 "uR"
224264 #endif
228268 #if VISEXPORT
229269 "v"
230270 #endif
271 #if IR
272 "i"
273 #endif
274
231275 , opt)) {
232276 optarg = NULL;
233277 optind += 1;
234278 } else {
279 fprintf(stderr, "\nOption error: -%s\n\n", opt);
235280 usage(argv[0]);
236 exit(0);
281 exit(1);
237282 }
238283
239284 switch (opt[0]) {
240 case 'o':
241 output_device = optarg;
242 break;
285 case 'o':
286 output_device = optarg;
287 break;
243288 case 'a':
244289 output_params = optarg;
245290 break;
254299 case 'c':
255300 include_codecs = optarg;
256301 break;
302 case 'C':
303 if (atoi(optarg) > 0) {
304 idle = atoi(optarg) * 1000;
305 }
306 break;
257307 case 'e':
258308 exclude_codecs = optarg;
259309 break;
260 case 'd':
310 case 'd':
261311 {
262312 char *l = strtok(optarg, "=");
263313 char *v = strtok(NULL, "=");
270320 if (!strcmp(l, "all") || !strcmp(l, "stream")) log_stream = new;
271321 if (!strcmp(l, "all") || !strcmp(l, "decode")) log_decode = new;
272322 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
273326 } else {
327 fprintf(stderr, "\nDebug settings error: -d %s\n\n", optarg);
274328 usage(argv[0]);
275 exit(0);
329 exit(1);
276330 }
277331 }
278 break;
332 break;
279333 case 'f':
280334 logfile = optarg;
281335 break;
283337 {
284338 int byte = 0;
285339 char *tmp;
286 char *t = strtok(optarg, ":");
287 while (t && byte < 6) {
288 mac[byte++] = (u8_t)strtoul(t, &tmp, 16);
289 t = strtok(NULL, ":");
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 }
290348 }
291349 }
350 break;
351 case 'M':
352 modelname = optarg;
292353 break;
293354 case 'r':
294355 {
348409 case 'p':
349410 rt_priority = atoi(optarg);
350411 if (rt_priority > 99 || rt_priority < 1) {
412 fprintf(stderr, "\nError: invalid priority: %s\n\n", optarg);
351413 usage(argv[0]);
352 exit(0);
353 }
414 exit(1);
415 }
416 break;
417 #endif
418 #if LINUX || FREEBSD
419 case 'P':
420 pidfile = optarg;
354421 break;
355422 #endif
356423 case 'l':
357424 list_devices();
358425 exit(0);
359426 break;
427 #if ALSA
428 case 'L':
429 list_mixers(output_device);
430 exit(0);
431 break;
432 #endif
360433 #if RESAMPLE
361434 case 'u':
362435 case 'R':
380453 visexport = true;
381454 break;
382455 #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
383476 #if LINUX || FREEBSD
384477 case 'z':
385478 daemonize = true;
388481 case 't':
389482 license();
390483 exit(0);
391 default:
392 break;
393 }
394 }
484 case '?':
485 usage(argv[0]);
486 exit(0);
487 default:
488 fprintf(stderr, "Arg error: %s\n", argv[optind]);
489 break;
490 }
491 }
395492
396493 // warn if command line includes something which isn't parsed
397494 if (optind < argc) {
495 fprintf(stderr, "\nError: command line argument error\n\n");
398496 usage(argv[0]);
399 exit(0);
497 exit(1);
400498 }
401499
402500 signal(SIGINT, sighandler);
433531 }
434532
435533 #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
436542 if (daemonize) {
437543 if (daemon(0, logfile ? 1 : 0)) {
438544 fprintf(stderr, "error daemonizing: %s\n", strerror(errno));
439545 }
440546 }
547
548 if (pidfp) {
549 fprintf(pidfp, "%d\n", getpid());
550 fclose(pidfp);
551 }
441552 #endif
442553
443554 #if WIN
450561 output_init_stdout(log_output, output_buf_size, output_params, rates, rate_delay);
451562 } else {
452563 #if ALSA
453 output_init_alsa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, rt_priority);
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);
454566 #endif
455567 #if PORTAUDIO
456 output_init_pa(log_output, output_device, output_buf_size, output_params, rates, rate_delay);
568 output_init_pa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, idle);
457569 #endif
458570 }
459571
475587 }
476588 #endif
477589
590 #if IR
591 if (lircrc) {
592 ir_init(log_ir, lircrc);
593 }
594 #endif
595
478596 if (name && namefile) {
479 printf("-n and -N option should not be used at same time\n");
480 exit(0);
481 }
482
483 slimproto(log_slimproto, server, mac, name, namefile);
484
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
485603 decode_close();
486604 stream_close();
487605
496614 #endif
497615 }
498616
617 #if IR
618 ir_close();
619 #endif
620
499621 #if WIN
500622 winsock_close();
501623 #endif
502624
625 #if LINUX || FREEBSD
626 if (pidfile) {
627 unlink(pidfile);
628 free(pidfile);
629 }
630 #endif
631
503632 exit(0);
504633 }
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
4848
4949 s32_t gainL = output.current_replay_gain ? gain(output.gainL, output.current_replay_gain) : output.gainL;
5050 s32_t gainR = output.current_replay_gain ? gain(output.gainR, output.current_replay_gain) : output.gainR;
51
51
52 if (output.invert) { gainL = -gainL; gainR = -gainR; }
53
5254 frames = _buf_used(outputbuf) / BYTES_PER_FRAME;
5355 silence = false;
5456
219221 fade_gain = to_gain((float)cur_f / (float)dur_f);
220222 gainL = gain(gainL, fade_gain);
221223 gainR = gain(gainR, fade_gain);
224 if (output.invert) { gainL = -gainL; gainR = -gainR; }
222225 }
223226 if (output.fade_dir == FADE_CROSS) {
224227 // cross fade requires special treatment - performed later based on these values
234237 }
235238 gainL = output.gainL;
236239 gainR = output.gainR;
240 if (output.invert) { gainL = -gainL; gainR = -gainR; }
237241 cross_ptr = (s32_t *)(output.fade_end + cur_f * BYTES_PER_FRAME);
238242 } else {
239243 LOG_INFO("unable to continue crossfade - too few samples");
332336 }
333337 }
334338
335 void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[]) {
339 void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[], unsigned idle) {
336340 unsigned i;
337341
338342 loglevel = level;
362366 dop_silence_frames((u32_t *)silencebuf_dop, MAX_SILENCE_FRAMES);
363367 )
364368
365 output.state = OUTPUT_STOPPED;
369 LOG_DEBUG("idle timeout: %u", idle);
370
371 output.state = idle ? OUTPUT_OFF: OUTPUT_STOPPED;
366372 output.device = device;
367373 output.fade = FADE_INACTIVE;
374 output.invert = false;
368375 output.error_opening = false;
376 output.idle_to = (u32_t) idle;
369377
370378 if (!rates[0]) {
371379 if (!test_open(output.device, output.supported_rates)) {
386394 }
387395 }
388396 if (!output.default_sample_rate) {
389 output.default_sample_rate = rates[0];
397 output.default_sample_rate = output.supported_rates[0];
390398 }
391399
392400 output.current_sample_rate = output.default_sample_rate;
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
2626 #include <alsa/asoundlib.h>
2727 #include <sys/mman.h>
2828 #include <malloc.h>
29 #include <math.h>
2930
3031 #define MAX_DEVICE_LEN 128
3132
4849 bool mmap;
4950 bool reopen;
5051 u8_t *write_buf;
52 const char *volume_mixer_name;
53 int volume_mixer_index;
5154 } alsa;
5255
5356 static snd_pcm_t *pcmp = NULL;
9295 printf("\n");
9396 }
9497
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
95265 static void *alsa_error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) {
96266 va_list args;
97267 if ((loglevel >= lINFO && err == 0) || loglevel >= lDEBUG) {
180350 LOG_ERROR("device name too long: %s", device);
181351 return -1;
182352 }
353
354 LOG_INFO("opening device at: %u", sample_rate);
183355
184356 bool retry;
185357 do {
379551 if (silence) {
380552 inputptr = (s32_t *) silencebuf_dop;
381553 }
382 update_dop_marker((u32_t *) inputptr, out_frames);
554 update_dop((u32_t *) inputptr, out_frames, output.invert && !silence);
383555 }
384556 )
385557
437609
438610 static void *output_thread(void *arg) {
439611 bool start = true;
440 bool output_off = false, probe_device = (arg != NULL);
612 bool output_off = (output.state == OUTPUT_OFF);
613 bool probe_device = (arg != NULL);
441614 int err;
442615
443616 while (running) {
547720 continue;
548721 }
549722
550 // restrict avail in writei mode as write_buf is restricted to period_size
551 if (!alsa.mmap) {
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 {
552728 avail = min(avail, alsa.period_size);
553729 }
554730
612788 static pthread_t thread;
613789
614790 void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[],
615 unsigned rate_delay, unsigned rt_priority) {
791 unsigned rate_delay, unsigned rt_priority, unsigned idle, char *volume_mixer, bool mixer_unmute) {
616792
617793 unsigned alsa_buffer = ALSA_BUFFER_TIME;
618794 unsigned alsa_period = ALSA_PERIOD_COUNT;
620796 bool alsa_mmap = true;
621797 bool alsa_reopen = false;
622798
799 char *volume_mixer_name = next_param(volume_mixer, ',');
800 char *volume_mixer_index = next_param(NULL, ',');
801
623802 char *t = next_param(params, ':');
624803 char *c = next_param(NULL, ':');
625804 char *s = next_param(NULL, ':');
642821 alsa.write_buf = NULL;
643822 alsa.format = 0;
644823 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
645830 output.format = 0;
646831 output.buffer = alsa_buffer;
647832 output.period = alsa_period;
661846
662847 snd_lib_error_set_handler((snd_lib_error_handler_t)alsa_error_handler);
663848
664 output_init_common(level, device, output_buf_size, rates);
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 }
665854
666855 #if LINUX
667856 // RT linux - aim to avoid pagefaults by locking memory:
672861 LOG_INFO("memory locked");
673862 }
674863
675 mallopt(M_TRIM_THRESHOLD, -1);
676 mallopt(M_MMAP_MAX, 0);
864 mallopt(M_TRIM_THRESHOLD, -1);
865 mallopt(M_MMAP_MAX, 0);
677866
678867 touch_memory(silencebuf, MAX_SILENCE_FRAMES * BYTES_PER_FRAME);
679868 touch_memory(outputbuf->buf, outputbuf->size);
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
5050 #endif
5151
5252 void list_devices(void) {
53 PaError err;
53 PaError err;
5454 int i;
5555
5656 if ((err = Pa_Initialize()) != paNoError) {
6666 }
6767 printf("\n");
6868
69 if ((err = Pa_Terminate()) != paNoError) {
69 if ((err = Pa_Terminate()) != paNoError) {
7070 LOG_WARN("error closing port audio: %s", Pa_GetErrorText(err));
7171 }
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;
7280 }
7381
7482 static int pa_device_id(const char *device) {
305313
306314 IF_DSD(
307315 if (output.dop) {
308 update_dop_marker((u32_t *) outputbuf->readp, out_frames);
316 update_dop((u32_t *) outputbuf->readp, out_frames, output.invert);
309317 }
310318 )
311319
318326 IF_DSD(
319327 if (output.dop) {
320328 buf = silencebuf_dop;
321 update_dop_marker((u32_t *) buf, out_frames);
329 update_dop((u32_t *) buf, out_frames, false); // don't invert silence
322330 }
323331 )
324332
376384 return ret;
377385 }
378386
379 void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay) {
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) {
380389 PaError err;
381390 unsigned latency = 0;
382391 int osx_playnice = -1;
403412
404413 LOG_INFO("requested latency: %u", output.latency);
405414
406 if ((err = Pa_Initialize()) != paNoError) {
415 if ((err = Pa_Initialize()) != paNoError) {
407416 LOG_WARN("error initialising port audio: %s", Pa_GetErrorText(err));
408417 exit(0);
409418 }
410419
411 output_init_common(level, device, output_buf_size, rates);
420 output_init_common(level, device, output_buf_size, rates, idle);
412421
413422 LOCK;
414423
433442 }
434443 }
435444
436 if ((err = Pa_Terminate()) != paNoError) {
445 if ((err = Pa_Terminate()) != paNoError) {
437446 LOG_WARN("error closing port audio: %s", Pa_GetErrorText(err));
438447 }
439448
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
6666 if (silence) {
6767 obuf = silencebuf_dop;
6868 }
69 update_dop_marker((u32_t *)obuf, out_frames);
69 update_dop((u32_t *)obuf, out_frames, output.invert && !silence);
7070 }
7171 )
7272
149149 rates[0] = 44100;
150150 }
151151
152 output_init_common(level, "-", output_buf_size, rates);
152 output_init_common(level, "-", output_buf_size, rates, 0);
153153
154154 #if LINUX || OSX || FREEBSD
155155 pthread_attr_t attr;
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
103103 ptr += 8;
104104 _buf_inc_readp(streambuf, ptr - streambuf->readp);
105105 audio_left = len;
106 LOG_INFO("audio size: %u", audio_left);
106107 limit = true;
107108 return;
108109 }
112113 // following 4 bytes is blocksize - ignored
113114 ptr += 8 + 8;
114115 _buf_inc_readp(streambuf, ptr + offset - streambuf->readp);
115 audio_left = len;
116 audio_left = len - 8 - offset;
117 LOG_INFO("audio size: %u", audio_left);
116118 limit = true;
117119 return;
118120 }
188190 if (decode.new_stream) {
189191 LOG_INFO("setting track_start");
190192 LOCK_O_not_direct;
191 output.next_sample_rate = decode_newstream(sample_rate, output.supported_rates);
193 output.next_sample_rate = decode_newstream(sample_rate, output.supported_rates);
192194 output.track_start = outputbuf->writep;
193195 IF_DSD( output.next_dop = false; )
194196 if (output.fade_mode) _checkfade(true);
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
7373 SOXR(r, process, r->resampler, process->inbuf, process->in_frames, &idone, process->outbuf, process->max_out_frames, &odone);
7474 if (error) {
7575 LOG_INFO("soxr_process error: %s", soxr_strerror(error));
76 return;
7677 }
7778
7879 if (idone != process->in_frames) {
99100 soxr_error_t error = SOXR(r, process, r->resampler, NULL, 0, NULL, process->outbuf, process->max_out_frames, &odone);
100101 if (error) {
101102 LOG_INFO("soxr_process error: %s", soxr_strerror(error));
103 return true;
102104 }
103105
104106 process->out_frames = odone;
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
4545 extern struct decodestate decode;
4646
4747 extern struct codec *codecs[];
48 #if IR
49 extern struct irstate ir;
50 #endif
4851
4952 event_event wake_e;
5053
5457 #define UNLOCK_O mutex_unlock(outputbuf->mutex)
5558 #define LOCK_D mutex_lock(decode.mutex)
5659 #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
5764
5865 static struct {
5966 u32_t updated;
100107 }
101108
102109 static void sendHELO(bool reconnect, const char *fixed_cap, const char *var_cap, u8_t mac[6]) {
103 const char *base_cap = "Model=squeezelite,ModelName=SqueezeLite,AccuratePlayPoints=1,HasDigitalOut=1";
110 const char *base_cap = "Model=squeezelite,AccuratePlayPoints=1,HasDigitalOut=1,HasPolarityInversion=1,Firmware=" VERSION;
104111 struct HELO_packet pkt;
105112
106113 memset(&pkt, 0, sizeof(pkt));
225232 send_packet((u8_t *)name, strlen(name) + 1);
226233 }
227234
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
228252 static void process_strm(u8_t *pkt, int len) {
229253 struct strm_packet *strm = (struct strm_packet *)pkt;
230254
256280 unsigned interval = unpackN(&strm->replay_gain);
257281 LOCK_O;
258282 output.pause_frames = interval * status.current_sample_rate / 1000;
259 output.state = interval ? OUTPUT_PAUSE_FRAMES : OUTPUT_STOPPED;
283 if (interval) {
284 output.state = OUTPUT_PAUSE_FRAMES;
285 } else {
286 output.state = OUTPUT_STOPPED;
287 output.stop_time = gettime_ms();
288 }
260289 UNLOCK_O;
261290 if (!interval) sendSTAT("STMp", 0);
262291 LOG_DEBUG("pause interval: %u", interval);
279308 output.state = jiffies ? OUTPUT_START_AT : OUTPUT_RUNNING;
280309 output.start_at = jiffies;
281310 UNLOCK_O;
282 LOCK_D;
283 decode.state = DECODE_RUNNING;
284 UNLOCK_D;
285311 LOG_DEBUG("unpause at: %u now: %u", jiffies, gettime_ms());
286312 sendSTAT("STMr", 0);
287313 }
326352 output.next_replay_gain = unpackN(&strm->replay_gain);
327353 output.fade_mode = strm->transition_type - '0';
328354 output.fade_secs = strm->transition_period;
355 output.invert = (strm->flags & 0x03) == 0x03;
329356 LOG_DEBUG("set fade mode: %u", output.fade_mode);
330357 UNLOCK_O;
331358 }
370397 if (!aude->enable_spdif && output.state != OUTPUT_OFF) {
371398 output.state = OUTPUT_OFF;
372399 }
373 if (aude->enable_spdif && output.state == OUTPUT_OFF) {
400 if (aude->enable_spdif && output.state == OUTPUT_OFF && !output.idle_to) {
374401 output.state = OUTPUT_STOPPED;
402 output.stop_time = gettime_ms();
375403 }
376404 UNLOCK_O;
377405 }
383411
384412 LOG_DEBUG("audg gainL: %u gainR: %u adjust: %u", audg->gainL, audg->gainR, audg->adjust);
385413
386 LOCK_O;
387 output.gainL = audg->adjust ? audg->gainL : FIXED_ONE;
388 output.gainR = audg->adjust ? audg->gainR : FIXED_ONE;
389 UNLOCK_O;
414 set_volume(audg->adjust ? audg->gainL : FIXED_ONE, audg->adjust ? audg->gainR : FIXED_ONE);
390415 }
391416
392417 static void process_setd(u8_t *pkt, int len) {
563588 bool _sendSTMo = false;
564589 bool _sendSTMn = false;
565590 bool _stream_disconnect = false;
591 bool _start_output = false;
592 decode_state _decode_state;
566593 disconnect_code disconnect_code;
567594 static char header[MAX_HEADER];
568595 size_t header_len = 0;
596 #if IR
597 bool _sendIR = false;
598 u32_t ir_code, ir_ts;
599 #endif
569600 last = now;
570601
571602 LOCK_S;
593624 stream.meta_send = false;
594625 }
595626 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;
596651
597652 LOCK_O;
598653 status.output_full = _buf_used(outputbuf);
613668 output.pa_reopen = false;
614669 }
615670 #endif
616 if (output.state == OUTPUT_RUNNING && !sentSTMu && status.output_full == 0 && status.stream_state <= DISCONNECT) {
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) {
617676 _sendSTMu = true;
618677 sentSTMu = true;
678 LOG_DEBUG("output underrun");
679 output.state = OUTPUT_STOPPED;
680 output.stop_time = now;
619681 }
620682 if (output.state == OUTPUT_RUNNING && !sentSTMo && status.output_full == 0 && status.stream_state == STREAMING_HTTP) {
621683 _sendSTMo = true;
622684 sentSTMo = true;
623685 }
624 UNLOCK_O;
625
626 LOCK_D;
627 if (decode.state == DECODE_RUNNING && now - status.last > 1000) {
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) {
628691 _sendSTMt = true;
629692 status.last = now;
630693 }
631 if ((status.stream_state == STREAMING_HTTP || status.stream_state == STREAMING_FILE) && !sentSTMl
632 && decode.state == DECODE_STOPPED) {
633 if (autostart == 0) {
634 _sendSTMl = true;
635 sentSTMl = true;
636 } else if (autostart == 1) {
637 decode.state = DECODE_RUNNING;
638 LOCK_O;
639 if (output.state == OUTPUT_STOPPED) {
640 output.state = OUTPUT_BUFFER;
641 }
642 UNLOCK_O;
643 }
644 // autostart 2 and 3 require cont to be received first
645 }
646 if (decode.state == DECODE_COMPLETE || decode.state == DECODE_ERROR) {
647 if (decode.state == DECODE_COMPLETE) _sendSTMd = true;
648 if (decode.state == DECODE_ERROR) _sendSTMn = true;
649 decode.state = DECODE_STOPPED;
650 if (status.stream_state == STREAMING_HTTP || status.stream_state == STREAMING_FILE) {
651 _stream_disconnect = true;
652 }
653 }
654 UNLOCK_D;
655
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
656707 if (_stream_disconnect) stream_disconnect();
657708
658709 // send packets once locks released as packet sending can block
666717 if (_sendSTMn) sendSTAT("STMn", 0);
667718 if (_sendRESP) sendRESP(header, header_len);
668719 if (_sendMETA) sendMETA(header, header_len);
720 #if IR
721 if (_sendIR) sendIR(ir_code, ir_ts);
722 #endif
669723 }
670724 }
671725 }
676730 }
677731
678732 in_addr_t discover_server(void) {
679 struct sockaddr_in d;
680 struct sockaddr_in s;
733 struct sockaddr_in d;
734 struct sockaddr_in s;
681735 char *buf;
682736 struct pollfd pollinfo;
683737
689743 buf = "e";
690744
691745 memset(&d, 0, sizeof(d));
692 d.sin_family = AF_INET;
746 d.sin_family = AF_INET;
693747 d.sin_port = htons(PORT);
694 d.sin_addr.s_addr = htonl(INADDR_BROADCAST);
748 d.sin_addr.s_addr = htonl(INADDR_BROADCAST);
695749
696750 pollinfo.fd = disc_sock;
697751 pollinfo.events = POLLIN;
719773 return s.sin_addr.s_addr;
720774 }
721775
722 void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile) {
723 struct sockaddr_in serv_addr;
724 static char fixed_cap[128], var_cap[128] = "";
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] = "";
725782 bool reconnect = false;
726783 unsigned failed_connect = 0;
727784 unsigned slimproto_port = 0;
771828 if (!running) return;
772829
773830 LOCK_O;
774 sprintf(fixed_cap, ",MaxSampleRate=%u", output.supported_rates[0]);
831 snprintf(fixed_cap, FIXED_CAP_LEN, ",ModelName=%s,MaxSampleRate=%u", modelname ? modelname : MODEL_NAME_STRING,
832 output.supported_rates[0]);
775833
776834 for (i = 0; i < MAX_CODECS; i++) {
777 if (codecs[i] && codecs[i]->id && strlen(fixed_cap) < 128 - 10) {
835 if (codecs[i] && codecs[i]->id && strlen(fixed_cap) < FIXED_CAP_LEN - 10) {
778836 strcat(fixed_cap, ",");
779837 strcat(fixed_cap, codecs[i]->types);
780838 }
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
8787 // data
8888 };
8989
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
90101 // from S:P:Squeezebox stream_s
91102 struct strm_packet {
92103 char opcode[4];
154165 // codec open - this is an extension to slimproto to allow the server to read the header and then return decode params
155166 struct codc_packet {
156167 char opcode[4];
157 u8_t format;
168 u8_t format;
158169 u8_t pcm_sample_size;
159170 u8_t pcm_sample_rate;
160171 u8_t pcm_channels;
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
1717 *
1818 */
1919
20 // make may define: PORTAUDIO, SELFPIPE, RESAMPLE, RESAMPLE_MP, VISEXPORT, DSD, LINKALL to influence build
21
22 #define VERSION "v1.6.4"
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)
2331
2432 // build detection
2533 #if defined(linux)
99107 #define VISEXPORT 0
100108 #endif
101109
110 #if LINUX && defined(IR)
111 #undef IR
112 #define IR 1
113 #else
114 #define IR 0
115 #endif
116
102117 #if defined(DSD)
103118 #undef DSD
104119 #define DSD 1
132147 #define LIBAVCODEC "libavcodec.so.%d"
133148 #define LIBAVFORMAT "libavformat.so.%d"
134149 #define LIBSOXR "libsoxr.so.0"
150 #define LIBLIRC "liblirc_client.so.0"
135151 #endif
136152
137153 #if OSX
212228 #define STREAM_THREAD_STACK_SIZE 64 * 1024
213229 #define DECODE_THREAD_STACK_SIZE 128 * 1024
214230 #define OUTPUT_THREAD_STACK_SIZE 64 * 1024
231 #define IR_THREAD_STACK_SIZE 64 * 1024
215232 #define thread_t pthread_t;
216233 #define closesocket(s) close(s)
217234 #define last_error() errno
408425 void buf_destroy(struct buffer *buf);
409426
410427 // slimproto.c
411 void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile);
428 void slimproto(log_level level, char *server, u8_t mac[6], const char *name, const char *namefile, const char *modelname);
412429 void slimproto_stop(void);
413430 void wake_controller(void);
414431
439456 bool stream_disconnect(void);
440457
441458 // decode.c
442 typedef enum { DECODE_STOPPED = 0, DECODE_RUNNING, DECODE_COMPLETE, DECODE_ERROR } decode_state;
459 typedef enum { DECODE_STOPPED = 0, DECODE_READY, DECODE_RUNNING, DECODE_COMPLETE, DECODE_ERROR } decode_state;
443460
444461 struct decodestate {
445462 decode_state state;
543560 u8_t *track_start; // set in decode thread
544561 u32_t gainL; // set by slimproto
545562 u32_t gainR; // set by slimproto
563 bool invert; // set by slimproto
546564 u32_t next_replay_gain; // set by slimproto
547565 unsigned threshold; // set by slimproto
548566 fade_state fade;
553571 unsigned fade_secs; // set by slimproto
554572 unsigned rate_delay;
555573 bool delay_active;
574 u32_t stop_time;
575 u32_t idle_to;
556576 #if DSD
557577 bool next_dop; // set in decode thread
558578 bool dop;
561581 #endif
562582 };
563583
564 void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[]);
584 void output_init_common(log_level level, const char *device, unsigned output_buf_size, unsigned rates[], unsigned idle);
565585 void output_close_common(void);
566586 void output_flush(void);
567587 // _* called with mutex locked
571591 // output_alsa.c
572592 #if ALSA
573593 void list_devices(void);
594 void list_mixers(const char *output_device);
595 void set_volume(unsigned left, unsigned right);
574596 bool test_open(const char *device, unsigned rates[]);
575597 void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[],
576 unsigned rate_delay, unsigned rt_priority);
598 unsigned rate_delay, unsigned rt_priority, unsigned idle, char *volume_mixer, bool mixer_unmute);
577599 void output_close_alsa(void);
578600 #endif
579601
580602 // output_pa.c
581603 #if PORTAUDIO
582604 void list_devices(void);
605 void set_volume(unsigned left, unsigned right);
583606 bool test_open(const char *device, unsigned rates[]);
584 void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay);
607 void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay, unsigned idle);
585608 void output_close_pa(void);
586609 void _pa_open(void);
587610 #endif
610633 // dop.c
611634 #if DSD
612635 bool is_flac_dop(u32_t *lptr, u32_t *rptr, frames_t frames);
613 void update_dop_marker(u32_t *ptr, frames_t frames);
636 void update_dop(u32_t *ptr, frames_t frames, bool invert);
614637 void dop_silence_frames(u32_t *ptr, frames_t frames);
615638 void dop_init(bool enable, unsigned delay);
616639 #endif
626649 struct codec *register_faad(void);
627650 struct codec *register_dsd(void);
628651 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
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
366366 }
367367
368368 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;
369 struct sockaddr_in addr;
370370
371371 int sock = socket(AF_INET, SOCK_STREAM, 0);
372372
385385 set_nonblock(sock);
386386 set_nosigpipe(sock);
387387
388 if (connect_timeout(sock, (struct sockaddr *) &addr, sizeof(addr), 10) < 0) {
388 if (connect_timeout(sock, (struct sockaddr *) &addr, sizeof(addr), 10) < 0) {
389389 LOG_INFO("unable to connect to server");
390390 LOCK;
391391 stream.state = DISCONNECT;
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by
6969 static char *str = NULL;
7070 char *ptr, *ret;
7171 if (src) str = src;
72 if (str && (ptr = strchr(str, c))) {
72 if (str && (ptr = strchr(str, c))) {
7373 ret = str;
7474 *ptr = '\0';
7575 str = ptr + 1;
102102 #if LINUX
103103 // search first 4 interfaces returned by IFCONF
104104 void get_mac(u8_t mac[]) {
105 struct ifconf ifc;
106 struct ifreq *ifr, *ifend;
107 struct ifreq ifreq;
108 struct ifreq ifs[4];
105 struct ifconf ifc;
106 struct ifreq *ifr, *ifend;
107 struct ifreq ifreq;
108 struct ifreq ifs[4];
109109
110110 mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0;
111111
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) {
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) {
118118 ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
119119
120120 for (ifr = ifc.ifc_req; ifr < ifend; ifr++) {
162162 #if WIN
163163 #pragma comment(lib, "IPHLPAPI.lib")
164164 void get_mac(u8_t mac[]) {
165 IP_ADAPTER_INFO AdapterInfo[16];
166 DWORD dwBufLen = sizeof(AdapterInfo);
167 DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen);
165 IP_ADAPTER_INFO AdapterInfo[16];
166 DWORD dwBufLen = sizeof(AdapterInfo);
167 DWORD dwStatus = GetAdaptersInfo(AdapterInfo, &dwBufLen);
168168
169169 mac[0] = mac[1] = mac[2] = mac[3] = mac[4] = mac[5] = 0;
170170
315315
316316 #if WIN
317317 void winsock_init(void) {
318 WSADATA wsaData;
318 WSADATA wsaData;
319319 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 }
320 int WSerr = WSAStartup(wVersionRequested, &wsaData);
321 if (WSerr != 0) {
322 LOG_ERROR("Bad winsock version");
323 exit(1);
324 }
325325 }
326326
327327 void winsock_close(void) {
365365 tv.tv_usec = 1000 * (timeout % 1000);
366366
367367 ret = select(fds[0].fd + 1, &r, &w, NULL, &tv);
368
368
369369 if (ret < 0) return ret;
370370
371371 fds[0].revents = 0;
382382 u8_t *ptr;
383383 for (ptr = buf; ptr < buf + size; ptr += sysconf(_SC_PAGESIZE)) {
384384 *ptr = 0;
385 }
386 }
387 #endif
385 }
386 }
387 #endif
00 /*
11 * Squeezelite - lightweight headless squeezebox emulator
22 *
3 * (c) Adrian Smith 2012-2014, triode1@btinternet.com
3 * (c) Adrian Smith 2012-2015, triode1@btinternet.com
44 *
55 * This program is free software: you can redistribute it and/or modify
66 * it under the terms of the GNU General Public License as published by