shairport_sync_SOURCES = shairport.c rtsp.c mdns.c mdns_external.c common.c rtp.c player.c alac.c audio.c
AM_CFLAGS = -Wno-multichar -DSYSCONFDIR=\"$(sysconfdir)\"
+AM_CPPFLAGS = -Wno-multichar -DSYSCONFDIR=\"$(sysconfdir)\"
+
+if USE_APPLE_ALAC
+ shairport_sync_SOURCES += apple_alac.cpp
+endif
if USE_CUSTOMPIDDIR
AM_CFLAGS+= \
--- /dev/null
+#include <string.h>
+
+// these are headers for the ALAC decoder, utilities and endian utilities
+#include <alac/ALACDecoder.h>
+#include <alac/ALACBitUtilities.h>
+#include <alac/EndianPortable.h>
+
+#include "config.h"
+#include "apple_alac.h"
+
+typedef struct magicCookie {
+ ALACSpecificConfig config;
+ ALACAudioChannelLayout channelLayoutInfo; // seems to be unused
+} magicCookie;
+
+magicCookie cookie;
+ALACDecoder * theDecoder;
+
+extern "C" int apple_alac_init(int frame_size,int sample_size,int sample_rate) {
+
+ memset(&cookie,0,sizeof(magicCookie));
+
+ #define CHANNELS_PER_FRAME 2
+
+ //create a magic cookie for the decoder
+
+ cookie.config.frameLength = Swap32NtoB(frame_size);
+ cookie.config.compatibleVersion = 0;
+ cookie.config.bitDepth = sample_size;
+ cookie.config.pb = 40;
+ cookie.config.mb = 10;
+ cookie.config.kb = 14;
+ cookie.config.numChannels = CHANNELS_PER_FRAME;
+ cookie.config.maxRun = Swap16NtoB(255);
+ cookie.config.maxFrameBytes = 0;
+ cookie.config.avgBitRate = 0;
+ cookie.config.sampleRate = Swap32NtoB(sample_rate);
+
+ theDecoder = new ALACDecoder;
+ theDecoder->Init(&cookie, sizeof(magicCookie));
+
+ return 0;
+}
+
+extern "C" int apple_alac_decode_frame(unsigned char *sampleBuffer, uint32_t bufferLength, unsigned char *dest, int *outsize)
+{
+ uint32_t numFrames = 0;
+ BitBuffer theInputBuffer;
+ BitBufferInit(&theInputBuffer, sampleBuffer, bufferLength);
+ theDecoder->Decode(&theInputBuffer, dest, Swap32BtoN(cookie.config.frameLength), CHANNELS_PER_FRAME, &numFrames);
+ *outsize = numFrames;
+ return 0;
+}
+
+extern "C" int apple_alac_terminate() {
+ delete(theDecoder);
+}
+
--- /dev/null
+#ifndef __APPLE_ALAC_H
+#define __APPLE_ALAC_H
+
+#include <stdint.h>
+#include "config.h"
+
+#ifdef __cplusplus
+ #define EXTERNC extern "C"
+#else
+ #define EXTERNC
+#endif
+
+
+EXTERNC int apple_alac_init(int frame_size,int sample_size,int sample_rate);
+EXTERNC int apple_alac_terminate();
+EXTERNC int apple_alac_decode_frame(unsigned char *sampleBuffer, uint32_t bufferLength, unsigned char *dest, int *outsize);
+
+#undef EXTERNC
+
+#endif /* __APPLE_ALAC_H */
+
#endif
#endif
-#include "common.h"
#include <libdaemon/dlog.h>
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
enum playback_mode_type {
ST_stereo = 0,
ST_mono,
+ ST_reverse_stereo,
+ ST_left_only,
+ ST_right_only,
} playback_mode_type;
+enum decoders_supported_type {
+ decoder_hammerton = 0,
+ decoder_apple_alac,
+} decoders_supported_type;
// the following enum must _exactly match the snd_pcm_format_t definition in the alsa pcm.h file.
int cmd_blocking;
int tolerance; // allow this much drift before attempting to correct it
enum stuffing_type packet_stuffing;
+ int decoders_supported;
+ int use_apple_decoder; // set to 1 if you want to use the apple decoder instead of the original by David Hammerton
char *pidfile;
// char *logfile;
// char *errfile;
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.50])
-AC_INIT([shairport-sync], [3.0PB1], [mikebrady@eircom.net])
+AC_INIT([shairport-sync], [3.0PB2], [mikebrady@eircom.net])
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([shairport.c])
AC_CONFIG_HEADERS([config.h])
# Checks for programs.
AC_PROG_CC
+AC_PROG_CXX
AC_PROG_INSTALL
PKG_PROG_PKG_CONFIG([0.9.0])
[ --with-configfiles = install configuration files during a make install ], ,[with_configfiles=yes])
AM_CONDITIONAL([INSTALL_CONFIG_FILES], [test "x$with_configfiles" = "xyes"])
+# Look for Apple ALAC flag
+AC_ARG_WITH(apple-alac, [ --with-apple-alac = include support for the Apple ALAC decoder],
+ [AC_MSG_RESULT(>>Including the Apple ALAC Decoder)
+ HAS_APPLE_ALAC=1
+ AM_CONDITIONAL([USE_APPLE_ALAC], [test 0])
+ AC_DEFINE([CONFIG_APPLE_ALAC], 1, [Needed by the compiler.])
+ if test "x${with_pkg_config}" = xyes ; then
+ PKG_CHECK_MODULES(
+ [ALAC], [alac],
+ [LIBS="${ALAC_LIBS} ${LIBS}"
+ AC_DEFINE([HAVE_APPLE_ALAC],[1],[Define to 1 if you are using the Apple ALAC Decoder])])
+ else
+ AC_CHECK_LIB([alac], [BitBufferInit], , AC_MSG_ERROR(Apple ALAC Decoder support requires the alac library!))
+ AC_DEFINE([HAVE_APPLE_ALAC],[1],[Define to 1 if you have the Apple ALAC library])
+ fi ])
+AM_CONDITIONAL([USE_APPLE_ALAC], [test "x$HAS_APPLE_ALAC" = "x1"])
# Look for piddir flag
AC_ARG_WITH(piddir, [ --with-piddir=<pathname> Specify a pathname to a directory in which to write the PID file.], [
#include "alac.h"
+#include "apple_alac.h"
+
// parameters from the source
static unsigned char *aesiv;
#ifdef HAVE_LIBSSL
static int late_packet_message_sent;
static uint64_t packet_count = 0;
static int32_t last_seqno_read;
+static int decoder_in_use = 0;
// interthread variables
static int fix_volume = 0x10000;
}
unsigned char packet[MAX_PACKET];
unsigned char packetp[MAX_PACKET];
- // assert(len <= MAX_PACKET);
+ assert(len <= MAX_PACKET);
int reply = 0; //everything okay
int outsize=bytes_per_audio_frame*(*destlen); // the size the output should be, in bytes
int toutsize = outsize;
AES_cbc_encrypt(buf, packet, aeslen, &aes, iv, AES_DECRYPT);
#endif
memcpy(packet + aeslen, buf + aeslen, len - aeslen);
- alac_decode_frame(decoder_info, packet, dest, &outsize);
+#ifdef HAVE_APPLE_ALAC
+ if (config.use_apple_decoder) {
+ if (decoder_in_use!=1<<decoder_apple_alac) {
+ debug(1,"Apple ALAC Decoder used on encrypted audio.");
+ decoder_in_use=1<<decoder_apple_alac;
+ }
+ apple_alac_decode_frame(packet, len, (unsigned char *) dest, &outsize);
+ outsize=outsize*4; // bring the size to bytes
+ } else
+#endif
+ {
+ if (decoder_in_use!=1<<decoder_hammerton) {
+ debug(1,"Hammerton Decoder used on encrypted audio.");
+ decoder_in_use=1<<decoder_hammerton;
+ }
+ alac_decode_frame(decoder_info, packet, (unsigned char *) dest, &outsize);
+ }
} else {
- alac_decode_frame(decoder_info, buf, dest, &outsize);
+ // not encrypted
+#ifdef HAVE_APPLE_ALAC
+ if (config.use_apple_decoder) {
+ if (decoder_in_use!=1<<decoder_apple_alac) {
+ debug(1,"Apple ALAC Decoder used on unencrypted audio.");
+ decoder_in_use=1<<decoder_apple_alac;
+ }
+ apple_alac_decode_frame(buf, len, (unsigned char *) dest, &outsize);
+ outsize=outsize*4; // bring the size to bytes
+ } else
+#endif
+ {
+ if (decoder_in_use!=1<<decoder_hammerton) {
+ debug(1,"Hammerton Decoder used on unencrypted audio.");
+ decoder_in_use=1<<decoder_hammerton;
+ }
+ alac_decode_frame(decoder_info, buf, dest, &outsize);
+ }
}
if(outsize>toutsize) {
debug(2,"Output from alac_decode larger (%d bytes, not frames) than expected (%d bytes) -- truncated, but buffer overflow possible! Encrypted = %d.",outsize, toutsize, encrypted);
reply = -1; // output packet is the wrong size
- }
+ }
+
*destlen = outsize / bytes_per_audio_frame;
if ((outsize % bytes_per_audio_frame)!=0)
debug(1,"Number of audio frames (%d) does not correspond exactly to the number of bytes (%d) and the audio frame size (%d).",*destlen,outsize,bytes_per_audio_frame);
alac->setinfo_86 = fmtp[10];
alac->setinfo_8a_rate = fmtp[11];
alac_allocate_buffers(alac);
+
+#ifdef HAVE_APPLE_ALAC
+ apple_alac_init(frame_size,sample_size,sampling_rate);
+#endif
+
return 0;
}
-static void free_decoder(void) { alac_free(decoder_info); }
+static void terminate_decoders(void) {
+ alac_free(decoder_info);
+#ifdef HAVE_APPLE_ALAC
+ apple_alac_terminate();
+#endif
+}
static void init_buffer(void) {
int i;
shutdown_requested = 1;
}
}
-
int rco = get_requested_connection_state_to_output();
if (connection_state_to_output != rco) {
flush_requested = 0;
}
pthread_mutex_unlock(&flush_mutex);
-
uint32_t flush_limit = 0;
if (ab_synced) {
do {
session_corrections = 0;
play_segment_reference_frame = 0; // zero signals that we are not in a play segment
-
+
int output_sample_ratio = 1;
if (config.output_rate!=0)
output_sample_ratio = config.output_rate/44100;
while (!please_stop) {
abuf_t *inframe = buffer_get_frame();
if (inframe) {
- if (inframe->data) {
+ inbuf = inframe->data;
+ if (inbuf) {
play_number++;
// if it's a supplied silent frame, let us know...
if (inframe->timestamp == 0) {
// debug(1,"Player has a supplied silent frame.");
last_seqno_read =
(SUCCESSOR(last_seqno_read) & 0xffff); // manage the packet out of sequence minder
- if (inframe->data==NULL)
- debug(1,"NULL inframe->data to play -- skipping it.");
+ if (inbuf==NULL)
+ debug(1,"NULL inbuf to play -- skipping it.");
else {
if (inframe->length==0)
- debug(1,"empty frame to play -- skipping it.");
+ debug(1,"empty frame to play -- skipping it (1).");
else
- config.output->play(inframe->data, inframe->length);
+ config.output->play(inbuf, inframe->length);
}
} else {
// We have a frame of data. We need to see if we want to add or remove a frame from it to
config.output->play(inframe->data, inframe->length);
}
} else {
-
-
+
+
#ifdef HAVE_LIBSOXR
switch (config.packet_stuffing) {
case ST_basic:
#endif
command_stop();
free_buffer();
- free_decoder();
+ terminate_decoders();
int rc = pthread_cond_destroy(&flowcontrol);
if (rc)
debug(1, "Error destroying condition variable.");
freeaddrinfo(info);
if (!nsock)
- die("Could not establish a service on port %d -- program terminating. Is another Shairport Sync running on this device?",config.port);
+ die("Could not establish a service on port %d -- program terminating. Is another instance of Shairport Sync running on this device?",config.port);
int maxfd = -1;
fd_set fds;
// audio_backend_buffer_desired_length = 6615; // If set too small, buffer underflow occurs on low-powered machines. Too long and the response times with software mixer become annoying.
// disable_synchronization = "no"; // Set to "yes" to disable synchronization. Default is "no".
// period_size = <number>; // Use this optional advanced setting to set the alsa period size near to this value
+// buffer_size = <number>; // Use this optional advanced setting to set the alsa buffer size near to this value
// use_mmap_if_available = "yes"; // Use this optional advanced setting to control whether MMAP-based output is used to communicate with the DAC. Default is "yes"
};
config.volume_range_db = value;
}
+ /* Get the use_apple_decoder setting. */
+ if (config_lookup_string(config.cfg, "general.use_apple_decoder", &str)) {
+ if (strcasecmp(str, "no") == 0)
+ config.use_apple_decoder = 0;
+ else if (strcasecmp(str, "yes") == 0) {
+ if ((config.decoders_supported & 1<<decoder_apple_alac)!=0)
+ config.use_apple_decoder = 1;
+ else
+ inform("Support for the Apple ALAC decoder has not been comiled into this version of Shairport Sync. The default decoder will be used.");
+ } else
+ die("Invalid use_apple_decoder option choice \"%s\". It should be \"yes\" or \"no\"");
+ }
+
/* Get the default latency. Deprecated! */
if (config_lookup_int(config.cfg, "latencies.default", &value))
config.userSuppliedLatency = value;
config.audio_backend_buffer_desired_length = 6615; // 0.15 seconds.
config.udp_port_base = 6001;
config.udp_port_range = 100;
+ config.decoders_supported = 1<<decoder_hammerton; // David Hammerton's decoder supported by default
+ #ifdef HAVE_APPLE_ALAC
+ config.decoders_supported += 1<<decoder_apple_alac;
+ #endif
/* Check if we are called with -V or --version parameter */
if (argc >= 2 && ((strcmp(argv[1], "-V") == 0) || (strcmp(argv[1], "--version") == 0))) {
debug(1, "audio backend latency offset is %d.", config.audio_backend_latency_offset);
debug(1, "volume range in dB (zero means use the range specified by the mixer): %u.", config.volume_range_db);
debug(1, "zeroconf regtype is \"%s\".", config.regtype);
+ debug(1, "decoders_supported field is %d.", config.decoders_supported);
+ debug(1, "use_apple_decoder is %d.", config.use_apple_decoder);
char *realConfigPath = realpath(config.configfile,NULL);
if (realConfigPath) {