]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Merge support of apple's ALAC decoder
authorMike Brady <mikebrady@eircom.net>
Sun, 28 Aug 2016 20:38:24 +0000 (21:38 +0100)
committerMike Brady <mikebrady@eircom.net>
Sun, 28 Aug 2016 20:38:24 +0000 (21:38 +0100)
Makefile.am
apple_alac.cpp [new file with mode: 0644]
apple_alac.h [new file with mode: 0644]
common.c
common.h
configure.ac
player.c
rtsp.c
scripts/shairport-sync.conf
shairport.c

index 3ad626090ba57c7ba63b210aa1a9eb35ed8e57a8..ff677f806e17887a941d91d87bdeeea625bd01b2 100644 (file)
@@ -4,6 +4,11 @@ bin_PROGRAMS = shairport-sync
 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+= \
diff --git a/apple_alac.cpp b/apple_alac.cpp
new file mode 100644 (file)
index 0000000..5c633fb
--- /dev/null
@@ -0,0 +1,58 @@
+#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);
+}
+
diff --git a/apple_alac.h b/apple_alac.h
new file mode 100644 (file)
index 0000000..61eb685
--- /dev/null
@@ -0,0 +1,21 @@
+#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 */
+
index bf7212738e03433876c05b235cbeb34b3e9d854e..39a936f57fcf6362c63e3e11cba52675c9a65e6b 100644 (file)
--- a/common.c
+++ b/common.c
@@ -67,7 +67,6 @@
 #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
index d67100e78e0f2d6071261c025eee108537a8f94e..e2ab47b9166e080633fc66eb282b241707267576 100644 (file)
--- a/common.h
+++ b/common.h
@@ -46,8 +46,15 @@ enum stuffing_type {
 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.
 
@@ -112,6 +119,8 @@ typedef struct {
   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;
index 5ba84ebc02212d4cbeea4bee1fc7ee7f7f1f29f3..51fa97d8b69816f5beccdd3db197abe3394a9038 100644 (file)
@@ -2,7 +2,7 @@
 # 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])
@@ -20,6 +20,7 @@ with_os_type=`echo ${with_os_type} | tr '[[:upper:]]' '[[:lower:]]' `
 
 # Checks for programs.
 AC_PROG_CC
+AC_PROG_CXX
 AC_PROG_INSTALL
 
 PKG_PROG_PKG_CONFIG([0.9.0])
@@ -93,6 +94,22 @@ AC_ARG_WITH([configfiles],
 [  --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.], [
index da1a312cc0ad973cfb5665c617e48cc050c5ac08..7572b89904660cffc3f14292989f51862ea1889d 100644 (file)
--- a/player.c
+++ b/player.c
@@ -67,6 +67,8 @@
 
 #include "alac.h"
 
+#include "apple_alac.h"
+
 // parameters from the source
 static unsigned char *aesiv;
 #ifdef HAVE_LIBSSL
@@ -100,6 +102,7 @@ static alac_file *decoder_info;
 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;
@@ -240,7 +243,7 @@ static int alac_decode(short *dest, int *destlen, uint8_t *buf, int len) {
   }
   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;
@@ -256,15 +259,49 @@ static int alac_decode(short *dest, int *destlen, uint8_t *buf, int len) {
     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);
@@ -298,10 +335,20 @@ static int init_decoder(int32_t fmtp[12]) {
   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;
@@ -506,7 +553,6 @@ static abuf_t *buffer_get_frame(void) {
         shutdown_requested = 1;
       }
     }
-
     int rco = get_requested_connection_state_to_output();
 
     if (connection_state_to_output != rco) {
@@ -530,7 +576,6 @@ static abuf_t *buffer_get_frame(void) {
       flush_requested = 0;
     }
     pthread_mutex_unlock(&flush_mutex);
-
     uint32_t flush_limit = 0;
     if (ab_synced) {
       do {
@@ -1000,7 +1045,7 @@ static void *player_thread_func(void *arg) {
 
        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;
@@ -1112,20 +1157,21 @@ static void *player_thread_func(void *arg) {
   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
@@ -1279,8 +1325,8 @@ static void *player_thread_func(void *arg) {
                   config.output->play(inframe->data, inframe->length);
               }
             } else {
-            
-            
+
+
 #ifdef HAVE_LIBSOXR
               switch (config.packet_stuffing) {
               case ST_basic:
@@ -1766,7 +1812,7 @@ void player_stop(pthread_t *player_thread) {
        #endif
                command_stop();
                free_buffer();
-               free_decoder();
+               terminate_decoders();
                int rc = pthread_cond_destroy(&flowcontrol);
                if (rc)
                        debug(1, "Error destroying condition variable.");
diff --git a/rtsp.c b/rtsp.c
index d2c71497d8ef47b463a59bab4ebe35015e110f11..3f6d9646d0e95588c9efe4202052e03bf4625914 100644 (file)
--- a/rtsp.c
+++ b/rtsp.c
@@ -1882,7 +1882,7 @@ void rtsp_listen_loop(void) {
   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;
index bb737ee84323216bc90a1bfa648f7a5bc8ff0dd4..3d7918d0ca8a3fb7150f754c9b2afa2efc1cb0e8 100644 (file)
@@ -62,6 +62,7 @@ alsa =
 //  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"
 };
 
index 1b97099b9c92c05718086710478dd32ba6f4df01..2ccaa9fb3ee18e337fda29dd33f09b1f9611f981 100644 (file)
@@ -441,6 +441,19 @@ int parse_options(int argc, char **argv) {
           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;
@@ -760,6 +773,10 @@ int main(int argc, char **argv) {
   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))) {
@@ -1023,6 +1040,8 @@ int main(int argc, char **argv) {
   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) {