diff --git a/ChangeLog.txt b/ChangeLog.txt
index d30adf4..7b6571b 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -103,3 +103,35 @@
Minor changes
- improve synchronisation feedback accuracy
+
+Version 1.6.5 21/11/14
+======================
+
+Minor changes
+- fix problem opening ALSA device if 44100 is not supported
+- trap setting of hw player mac address
+
+Version 1.7 1/1/15
+==================
+
+Minor changes
+- allow player modelname to be set at compile or run time
+- workaround alsa drivers reporting very large number of available frames
+- fix clicks on localfile playback of AIFF files
+- add -P option to store process id in a file
+- improve error messages for command line parsing
+
+Version 1.7.1 10/1/15
+=====================
+
+Minor changes
+- fix crash which could occur when resampling
+
+Version 1.8 1/2/15
+==================
+
+Features
+- support for closing output device when idle with -C option
+- support for basic IR input using LIRC on Linux
+- support for volume adjustment or unmuting of alsa mixer
+- support for inverting output polarity via LMS setting (requires recent 7.9 server)
diff --git a/LICENSE.txt b/LICENSE.txt
index 8bb3cc5..94af310 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
Squeezelite - lightweight headless squeezebox emulator
-(c) Adrian Smith 2012-2014, triode1@btinternet.com
+(c) Adrian Smith 2012-2015, triode1@btinternet.com
Released under GPLv3 license:
@@ -17,3 +17,33 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
+
+---------------------------------------------------------------------
+
+If built with DSD support, this software also includes code subject to the following license:
+
+Copyright 2009, 2011 Sebastian Gesemann. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY SEBASTIAN GESEMANN ''AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SEBASTIAN GESEMANN OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Sebastian Gesemann.
diff --git a/Makefile b/Makefile
index 65e224b..dca2abd 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,7 @@
OPT_LINKALL = -DLINKALL
OPT_RESAMPLE= -DRESAMPLE
OPT_VIS = -DVISEXPORT
+OPT_IR = -DIR
SOURCES = \
main.c slimproto.c buffer.c stream.c utils.c \
@@ -19,12 +20,14 @@
SOURCES_FF = ffmpeg.c
SOURCES_RESAMPLE = process.c resample.c
SOURCES_VIS = output_vis.c
+SOURCES_IR = ir.c
LINK_LINUX = -ldl
LINKALL = -lFLAC -lmad -lvorbisfile -lfaad -lmpg123
LINKALL_FF = -lavcodec -lavformat -lavutil
LINKALL_RESAMPLE = -lsoxr
+LINKALL_IR = -llirc_client
DEPS = squeezelite.h slimproto.h
@@ -43,6 +46,9 @@
ifneq (,$(findstring $(OPT_VIS), $(CFLAGS)))
SOURCES += $(SOURCES_VIS)
endif
+ifneq (,$(findstring $(OPT_IR), $(CFLAGS)))
+ SOURCES += $(SOURCES_IR)
+endif
# add optional link options
ifneq (,$(findstring $(OPT_LINKALL), $(CFLAGS)))
@@ -52,6 +58,9 @@
endif
ifneq (,$(findstring $(OPT_RESAMPLE), $(CFLAGS)))
LDFLAGS += $(LINKALL_RESAMPLE)
+endif
+ifneq (,$(findstring $(OPT_IR), $(CFLAGS)))
+ LDFLAGS += $(LINKALL_IR)
endif
else
# if not LINKALL and linux add LINK_LINUX
diff --git a/buffer.c b/buffer.c
index 814b012..fd83a76 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1,7 +1,7 @@
/*
* Squeezelite - lightweight headless squeezebox emulator
*
- * (c) Adrian Smith 2012-2014, triode1@btinternet.com
+ * (c) Adrian Smith 2012-2015, triode1@btinternet.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/decode.c b/decode.c
index 53bae8e..add5fcb 100644
--- a/decode.c
+++ b/decode.c
@@ -1,7 +1,7 @@
/*
* Squeezelite - lightweight headless squeezebox emulator
*
- * (c) Adrian Smith 2012-2014, triode1@btinternet.com
+ * (c) Adrian Smith 2012-2015, triode1@btinternet.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -240,6 +240,8 @@
codec->open(sample_size, sample_rate, channels, endianness);
+ decode.state = DECODE_READY;
+
UNLOCK_D;
return;
}
diff --git a/dop.c b/dop.c
index 580dd38..b498202 100644
--- a/dop.c
+++ b/dop.c
@@ -37,7 +37,7 @@
u32_t next = 0;
while (frames--) {
- if (((*lptr & 0x00FF0000) == 0x00050000 && (*rptr & 0x00FF0000) == 0x00050000) ||
+ if (((*lptr & 0x00FF0000) == 0x00050000 && (*rptr & 0x00FF0000) == 0x00050000) ||
((*lptr & 0x00FF0000) == 0x00FA0000 && (*rptr & 0x00FF0000) == 0x00FA0000)) {
if (*lptr >> 24 == next) {
matched++;
@@ -58,17 +58,28 @@
return false;
}
-// update the dop marker for frames in the output buffer
+// update the dop marker and potentially invert polarity for frames in the output buffer
// performaned on all output including silence to maintain marker phase consitency
-void update_dop_marker(u32_t *ptr, frames_t frames) {
+void update_dop(u32_t *ptr, frames_t frames, bool invert) {
static u32_t marker = 0x05;
- while (frames--) {
- u32_t scaled_marker = marker << 24;
- *ptr = (*ptr & 0x00FFFFFF) | scaled_marker;
- ++ptr;
- *ptr = (*ptr & 0x00FFFFFF) | scaled_marker;
- ++ptr;
- marker = ( 0x05 + 0xFA ) - marker;
+ if (!invert) {
+ while (frames--) {
+ u32_t scaled_marker = marker << 24;
+ *ptr = (*ptr & 0x00FFFFFF) | scaled_marker;
+ ++ptr;
+ *ptr = (*ptr & 0x00FFFFFF) | scaled_marker;
+ ++ptr;
+ marker = ( 0x05 + 0xFA ) - marker;
+ }
+ } else {
+ while (frames--) {
+ u32_t scaled_marker = marker << 24;
+ *ptr = ((~(*ptr)) & 0x00FFFFFF) | scaled_marker;
+ ++ptr;
+ *ptr = ((~(*ptr)) & 0x00FFFFFF) | scaled_marker;
+ ++ptr;
+ marker = ( 0x05 + 0xFA ) - marker;
+ }
}
}
diff --git a/dsd.c b/dsd.c
index 4b1662c..32d4795 100644
--- a/dsd.c
+++ b/dsd.c
@@ -1,7 +1,7 @@
/*
* Squeezelite - lightweight headless squeezebox emulator
*
- * (c) Adrian Smith 2012-2014, triode1@btinternet.com
+ * (c) Adrian Smith 2012-2015, triode1@btinternet.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/faad.c b/faad.c
index aa297dc..96d33ca 100644
--- a/faad.c
+++ b/faad.c
@@ -1,7 +1,7 @@
/*
* Squeezelite - lightweight headless squeezebox emulator
*
- * (c) Adrian Smith 2012-2014, triode1@btinternet.com
+ * (c) Adrian Smith 2012-2015, triode1@btinternet.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -228,7 +228,7 @@
// found media data, advance to start of first chunk and return
if (!strcmp(type, "mdat")) {
- _buf_inc_readp(streambuf, 8);
+ _buf_inc_readp(streambuf, 8);
a->pos += 8;
bytes -= 8;
if (play) {
diff --git a/ffmpeg.c b/ffmpeg.c
index b96e169..279f31d 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -1,7 +1,7 @@
/*
* Squeezelite - lightweight headless squeezebox emulator
*
- * (c) Adrian Smith 2012-2014, triode1@btinternet.com
+ * (c) Adrian Smith 2012-2015, triode1@btinternet.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -610,8 +610,8 @@
ff->avcodec_alloc_frame = dlsym(handle_codec, "avcodec_alloc_frame");
ff->avcodec_free_frame = dlsym(handle_codec, "avcodec_free_frame");
ff->avcodec_decode_audio4 = dlsym(handle_codec, "avcodec_decode_audio4");
- ff->av_init_packet = dlsym(handle_codec, "av_init_packet");
- ff->av_free_packet = dlsym(handle_codec, "av_free_packet");
+ ff->av_init_packet = dlsym(handle_codec, "av_init_packet");
+ ff->av_free_packet = dlsym(handle_codec, "av_free_packet");
if ((err = dlerror()) != NULL) {
LOG_INFO("dlerror: %s", err);
@@ -620,14 +620,14 @@
LOG_INFO("loaded "LIBAVCODEC" (%u.%u.%u)", LIBAVCODEC_VERSION_MAJOR, ff->avcodec_version() >> 16, (ff->avcodec_version() >> 8) & 0xff, ff->avcodec_version() & 0xff);
- ff->avformat_version = dlsym(handle_format, "avformat_version");
- ff->avformat_alloc_context = dlsym(handle_format, "avformat_alloc_context");
- ff->avformat_free_context = dlsym(handle_format, "avformat_free_context");
- ff->avformat_open_input = dlsym(handle_format, "avformat_open_input");
- ff->avformat_find_stream_info = dlsym(handle_format, "avformat_find_stream_info");
- ff->avio_alloc_context = dlsym(handle_format, "avio_alloc_context");
- ff->av_read_frame = dlsym(handle_format, "av_read_frame");
- ff->av_find_input_format= dlsym(handle_format, "av_find_input_format");
+ ff->avformat_version = dlsym(handle_format, "avformat_version");
+ ff->avformat_alloc_context = dlsym(handle_format, "avformat_alloc_context");
+ ff->avformat_free_context = dlsym(handle_format, "avformat_free_context");
+ ff->avformat_open_input = dlsym(handle_format, "avformat_open_input");
+ ff->avformat_find_stream_info = dlsym(handle_format, "avformat_find_stream_info");
+ ff->avio_alloc_context = dlsym(handle_format, "avio_alloc_context");
+ ff->av_read_frame = dlsym(handle_format, "av_read_frame");
+ ff->av_find_input_format= dlsym(handle_format, "av_find_input_format");
ff->av_register_all = dlsym(handle_format, "av_register_all");
if ((err = dlerror()) != NULL) {
diff --git a/ir.c b/ir.c
new file mode 100644
index 0000000..2355ee4
--- /dev/null
+++ b/ir.c
@@ -0,0 +1,248 @@
+/*
+ * Squeezelite - lightweight headless squeezebox emulator
+ *
+ * (c) Adrian Smith 2012-2015, triode1@btinternet.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+// ir thread - linux only
+
+#include "squeezelite.h"
+
+#if IR
+
+#include
+
+#define LIRC_CLIENT_ID "squeezelite"
+
+static log_level loglevel;
+
+struct irstate ir;
+
+static struct lirc_config *config = NULL;
+static sockfd fd = -1;
+
+static thread_type thread;
+
+#define LOCK_I mutex_lock(ir.mutex)
+#define UNLOCK_I mutex_unlock(ir.mutex)
+
+#if !LINKALL
+struct lirc {
+ // LIRC symbols to be dynamically loaded
+ int (* lirc_init)(char *prog, int verbose);
+ int (* lirc_deinit)(void);
+ int (* lirc_readconfig)(char *file, struct lirc_config **config, int (check) (char *s));
+ void (* lirc_freeconfig)(struct lirc_config *config);
+ int (* lirc_nextcode)(char **code);
+ int (* lirc_code2char)(struct lirc_config *config, char *code, char **string);
+};
+
+static struct lirc *i;
+#endif
+
+#if LINKALL
+#define LIRC(h, fn, ...) (lirc_ ## fn)(__VA_ARGS__)
+#else
+#define LIRC(h, fn, ...) (h)->lirc_##fn(__VA_ARGS__)
+#endif
+
+// cmds based on entires in Slim_Device_Remote.ir
+// these may appear as config entries in .lircrc files
+static struct {
+ char *cmd;
+ u32_t code;
+} cmdmap[] = {
+ { "voldown", 0x768900ff },
+ { "volup", 0x7689807f },
+ { "rew", 0x7689c03f },
+ { "fwd", 0x7689a05f },
+ { "pause", 0x768920df },
+ { "play", 0x768910ef },
+ { "power", 0x768940bf },
+ { "muting", 0x7689c43b },
+ { "power_on", 0x76898f70 },
+ { "power_off",0x76898778 },
+ { NULL, 0 },
+};
+
+// selected lirc namespace button names as defaults - some support repeat
+static struct {
+ char *lirc;
+ u32_t code;
+ bool repeat;
+} keymap[] = {
+ { "KEY_VOLUMEDOWN", 0x768900ff, true },
+ { "KEY_VOLUMEUP", 0x7689807f, true },
+ { "KEY_PREVIOUS", 0x7689c03f, false },
+ { "KEY_REWIND", 0x7689c03f, false },
+ { "KEY_NEXT", 0x7689a05f, false },
+ { "KEY_FORWARD", 0x7689a05f, false },
+ { "KEY_PAUSE", 0x768920df, true },
+ { "KEY_PLAY", 0x768910ef, false },
+ { "KEY_POWER", 0x768940bf, false },
+ { "KEY_MUTE", 0x7689c43b, false },
+ { NULL, 0 , false },
+};
+
+static u32_t ir_cmd_map(const char *c) {
+ int i;
+ for (i = 0; cmdmap[i].cmd; i++) {
+ if (!strcmp(c, cmdmap[i].cmd)) {
+ return cmdmap[i].code;
+ }
+ }
+ return 0;
+}
+
+static u32_t ir_key_map(const char *c, const char *r) {
+ int i;
+ for (i = 0; keymap[i].lirc; i++) {
+ if (!strcmp(c, keymap[i].lirc)) {
+ if (keymap[i].repeat || !strcmp(r, "00")) {
+ return keymap[i].code;
+ }
+ LOG_DEBUG("repeat suppressed");
+ break;
+ }
+ }
+ return 0;
+}
+
+static void *ir_thread() {
+ char *code;
+
+ while (fd > 0 && LIRC(i, nextcode, &code) == 0) {
+
+ u32_t now = gettime_ms();
+ u32_t ir_code = 0;
+
+ if (code == NULL) continue;
+
+ if (config) {
+ // allow lirc_client to decode then lookup cmd in our table
+ // we can only send one IR event to slimproto so break after first one
+ char *c;
+ while (LIRC(i, code2char, config, code, &c) == 0 && c != NULL) {
+ ir_code = ir_cmd_map(c);
+ if (ir_code) {
+ LOG_DEBUG("ir cmd: %s -> %x", c, ir_code);
+ }
+ }
+ }
+
+ if (!ir_code) {
+ // try to match on lirc button name if it is from the standard namespace
+ // this allows use of non slim remotes without a specific entry in .lircrc
+ char *b, *r;
+ strtok(code, " \n"); // discard
+ r = strtok(NULL, " \n"); // repeat count
+ b = strtok(NULL, " \n"); // key name
+ if (r && b) {
+ ir_code = ir_key_map(b, r);
+ LOG_DEBUG("ir lirc: %s [%s] -> %x", b, r, ir_code);
+ }
+ }
+
+ if (ir_code) {
+ LOCK_I;
+ if (ir.code) {
+ LOG_DEBUG("code dropped");
+ }
+ ir.code = ir_code;
+ ir.ts = now;
+ UNLOCK_I;
+ wake_controller();
+ }
+
+ free(code);
+ }
+
+ return 0;
+}
+
+#if !LINKALL
+static bool load_lirc() {
+ void *handle = dlopen(LIBLIRC, RTLD_NOW);
+ char *err;
+
+ if (!handle) {
+ LOG_INFO("dlerror: %s", dlerror());
+ return false;
+ }
+
+ i->lirc_init = dlsym(handle, "lirc_init");
+ i->lirc_deinit = dlsym(handle, "lirc_deinit");
+ i->lirc_readconfig = dlsym(handle, "lirc_readconfig");
+ i->lirc_freeconfig = dlsym(handle, "lirc_freeconfig");
+ i->lirc_nextcode = dlsym(handle, "lirc_nextcode");
+ i->lirc_code2char = dlsym(handle, "lirc_code2char");
+
+ if ((err = dlerror()) != NULL) {
+ LOG_INFO("dlerror: %s", err);
+ return false;
+ }
+
+ LOG_INFO("loaded "LIBLIRC);
+ return true;
+}
+#endif
+
+void ir_init(log_level level, char *lircrc) {
+ loglevel = level;
+
+#if !LINKALL
+ i = malloc(sizeof(struct lirc));
+ if (!i || !load_lirc()) {
+ return;
+ }
+#endif
+
+ fd = LIRC(i, init, LIRC_CLIENT_ID, 0);
+
+ if (fd > 0) {
+ if (LIRC(i, readconfig,lircrc, &config, NULL) != 0) {
+ LOG_WARN("error reading config: %s", lircrc);
+ }
+
+ mutex_create(ir.mutex);
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN + IR_THREAD_STACK_SIZE);
+ pthread_create(&thread, &attr, ir_thread, NULL);
+ pthread_attr_destroy(&attr);
+
+ } else {
+ LOG_WARN("failed to connect to lircd - ir processing disabled");
+ }
+}
+
+void ir_close(void) {
+ if (fd > 0) {
+ fd = -1;
+ if (config) {
+ LIRC(i, freeconfig, config);
+ }
+ LIRC(i, deinit);
+
+ pthread_cancel(thread);
+ pthread_join(thread, NULL);
+ mutex_destroy(ir.mutex);
+ }
+}
+
+#endif //#if IR
diff --git a/mad.c b/mad.c
index 1605bf9..30e2498 100644
--- a/mad.c
+++ b/mad.c
@@ -1,7 +1,7 @@
/*
* Squeezelite - lightweight headless squeezebox emulator
*
- * (c) Adrian Smith 2012-2014, triode1@btinternet.com
+ * (c) Adrian Smith 2012-2015, triode1@btinternet.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/main.c b/main.c
index 96c9154..5f3d48c 100644
--- a/main.c
+++ b/main.c
@@ -1,7 +1,7 @@
/*
* Squeezelite - lightweight headless squeezebox emulator
*
- * (c) Adrian Smith 2012-2014, triode1@btinternet.com
+ * (c) Adrian Smith 2012-2015, triode1@btinternet.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
#include
-#define TITLE "Squeezelite " VERSION ", Copyright 2012-2014 Adrian Smith."
+#define TITLE "Squeezelite " VERSION ", Copyright 2012-2015 Adrian Smith."
#define CODECS_BASE "flac,pcm,mp3,ogg,aac"
#if FFMPEG
@@ -58,14 +58,26 @@
" -a \t\tSpecify sample format (16|24|32) of output file when using -o - to output samples to stdout (interleaved little endian only)\n"
" -b :