commit 31cf954da5ad71ad5fe74bc8e912ad11a75cb758 Author: Preston Marshall Date: Mon Apr 11 00:09:42 2011 -0500 first commit diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..9a9016a0 --- /dev/null +++ b/INSTALL @@ -0,0 +1,11 @@ +Type 'make' to build the packet decoder, 'hairtunes'. +You need the following installed: + +openssl +libao +avahi (avahi-daemon running and avahi-publish-service on path) +Perl + +Perl modules (install from CPAN): +HTTP::Message +Crypt::OpenSSL::RSA diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..8ec08596 --- /dev/null +++ b/Makefile @@ -0,0 +1,4 @@ +CFLAGS = `pkg-config --cflags --libs ao openssl` + +hairtunes: hairtunes.c alac.c + gcc hairtunes.c alac.c -D__i386 -lm $(CFLAGS) -o hairtunes diff --git a/README b/README new file mode 100644 index 00000000..b481c5ad --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +Shairport v0.01 +============== +James Laird +April 5, 2011 + +What it is +---------- +This program emulates an Airport Express for the purpose of streaming music from +iTunes and compatible iPods. It implements a server for the Apple RAOP protocol. + +It probably supports multiple simultaneous streams, if your audio output chain +(as detected by libao) does so. + +How to use it +------------- +1. Make sure avahi-daemon is running and the prerequisites are installed (see +INSTALL). +2. Edit shairport.pl with your favourite text editor to set the access +point name and/or password, if desired. +3. `perl shairport.pl` + +The triangle-in-rectangle Airtunes logo will appear in the iTunes status bar of +any machine on the network, or on iPod play controls screen. Choose your access +point name to start streaming to the Shairport instance. + +Thanks +------ +Big thanks to David Hammerton for releasing an ALAC decoder, which is reproduced +here in full. +Thanks to everyone who has worked to reverse engineer the RAOP protocol - after +finding the keys, everything else was pretty much trivial. +Thanks also to Apple for obfuscating the private key in the ROM image, using a +scheme that made the deobfuscation code itself stand out like a flare. +Thanks to wtbw. diff --git a/alac.c b/alac.c new file mode 100644 index 00000000..cd0860a2 --- /dev/null +++ b/alac.c @@ -0,0 +1,1122 @@ +/* + * ALAC (Apple Lossless Audio Codec) decoder + * Copyright (c) 2005 David Hammerton + * All rights reserved. + * + * This is the actual decoder. + * + * http://crazney.net/programs/itunes/alac.html + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +static const int host_bigendian = 0; + +#include +#include +#include +#ifdef _WIN32 + #include "stdint_win.h" +#else + #include +#endif + +#include "alac.h" + +#define _Swap32(v) do { \ + v = (((v) & 0x000000FF) << 0x18) | \ + (((v) & 0x0000FF00) << 0x08) | \ + (((v) & 0x00FF0000) >> 0x08) | \ + (((v) & 0xFF000000) >> 0x18); } while(0) + +#define _Swap16(v) do { \ + v = (((v) & 0x00FF) << 0x08) | \ + (((v) & 0xFF00) >> 0x08); } while (0) + +struct {signed int x:24;} se_struct_24; +#define SignExtend24(val) (se_struct_24.x = val) + +void allocate_buffers(alac_file *alac) +{ + alac->predicterror_buffer_a = malloc(alac->setinfo_max_samples_per_frame * 4); + alac->predicterror_buffer_b = malloc(alac->setinfo_max_samples_per_frame * 4); + + alac->outputsamples_buffer_a = malloc(alac->setinfo_max_samples_per_frame * 4); + alac->outputsamples_buffer_b = malloc(alac->setinfo_max_samples_per_frame * 4); + + alac->uncompressed_bytes_buffer_a = malloc(alac->setinfo_max_samples_per_frame * 4); + alac->uncompressed_bytes_buffer_b = malloc(alac->setinfo_max_samples_per_frame * 4); +} + +void alac_set_info(alac_file *alac, char *inputbuffer) +{ + char *ptr = inputbuffer; + ptr += 4; /* size */ + ptr += 4; /* frma */ + ptr += 4; /* alac */ + ptr += 4; /* size */ + ptr += 4; /* alac */ + + ptr += 4; /* 0 ? */ + + alac->setinfo_max_samples_per_frame = *(uint32_t*)ptr; /* buffer size / 2 ? */ + if (!host_bigendian) + _Swap32(alac->setinfo_max_samples_per_frame); + ptr += 4; + alac->setinfo_7a = *(uint8_t*)ptr; + ptr += 1; + alac->setinfo_sample_size = *(uint8_t*)ptr; + ptr += 1; + alac->setinfo_rice_historymult = *(uint8_t*)ptr; + ptr += 1; + alac->setinfo_rice_initialhistory = *(uint8_t*)ptr; + ptr += 1; + alac->setinfo_rice_kmodifier = *(uint8_t*)ptr; + ptr += 1; + alac->setinfo_7f = *(uint8_t*)ptr; + ptr += 1; + alac->setinfo_80 = *(uint16_t*)ptr; + if (!host_bigendian) + _Swap16(alac->setinfo_80); + ptr += 2; + alac->setinfo_82 = *(uint32_t*)ptr; + if (!host_bigendian) + _Swap32(alac->setinfo_82); + ptr += 4; + alac->setinfo_86 = *(uint32_t*)ptr; + if (!host_bigendian) + _Swap32(alac->setinfo_86); + ptr += 4; + alac->setinfo_8a_rate = *(uint32_t*)ptr; + if (!host_bigendian) + _Swap32(alac->setinfo_8a_rate); + ptr += 4; + + allocate_buffers(alac); + +} + +/* stream reading */ + +/* supports reading 1 to 16 bits, in big endian format */ +static uint32_t readbits_16(alac_file *alac, int bits) +{ + uint32_t result; + int new_accumulator; + + result = (alac->input_buffer[0] << 16) | + (alac->input_buffer[1] << 8) | + (alac->input_buffer[2]); + + /* shift left by the number of bits we've already read, + * so that the top 'n' bits of the 24 bits we read will + * be the return bits */ + result = result << alac->input_buffer_bitaccumulator; + + result = result & 0x00ffffff; + + /* and then only want the top 'n' bits from that, where + * n is 'bits' */ + result = result >> (24 - bits); + + new_accumulator = (alac->input_buffer_bitaccumulator + bits); + + /* increase the buffer pointer if we've read over n bytes. */ + alac->input_buffer += (new_accumulator >> 3); + + /* and the remainder goes back into the bit accumulator */ + alac->input_buffer_bitaccumulator = (new_accumulator & 7); + + return result; +} + +/* supports reading 1 to 32 bits, in big endian format */ +static uint32_t readbits(alac_file *alac, int bits) +{ + int32_t result = 0; + + if (bits > 16) + { + bits -= 16; + result = readbits_16(alac, 16) << bits; + } + + result |= readbits_16(alac, bits); + + return result; +} + +/* reads a single bit */ +static int readbit(alac_file *alac) +{ + int result; + int new_accumulator; + + result = alac->input_buffer[0]; + + result = result << alac->input_buffer_bitaccumulator; + + result = result >> 7 & 1; + + new_accumulator = (alac->input_buffer_bitaccumulator + 1); + + alac->input_buffer += (new_accumulator / 8); + + alac->input_buffer_bitaccumulator = (new_accumulator % 8); + + return result; +} + +static void unreadbits(alac_file *alac, int bits) +{ + int new_accumulator = (alac->input_buffer_bitaccumulator - bits); + + alac->input_buffer += (new_accumulator >> 3); + + alac->input_buffer_bitaccumulator = (new_accumulator & 7); + if (alac->input_buffer_bitaccumulator < 0) + alac->input_buffer_bitaccumulator *= -1; +} + +/* various implementations of count_leading_zero: + * the first one is the original one, the simplest and most + * obvious for what it's doing. never use this. + * then there are the asm ones. fill in as necessary + * and finally an unrolled and optimised c version + * to fall back to + */ +#if 0 +/* hideously inefficient. could use a bitmask search, + * alternatively bsr on x86, + */ +static int count_leading_zeros(int32_t input) +{ + int i = 0; + while (!(0x80000000 & input) && i < 32) + { + i++; + input = input << 1; + } + return i; +} +#elif defined(__GNUC__) && (defined(_X86) || defined(__i386) || defined(i386)) +/* for some reason the unrolled version (below) is + * actually faster than this. yay intel! + */ +static int count_leading_zeros(int input) +{ + int output = 0; + if (!input) return 32; + __asm("bsr %1, %0\n" + : "=r" (output) + : "r" (input)); + return (0x1f - output); +} +#elif defined(_MSC_VER) && defined(_M_IX86) +static int count_leading_zeros(int input) +{ + int output = 0; + if (!input) return 32; + __asm + { + mov eax, input; + mov edx, 0x1f; + bsr ecx, eax; + sub edx, ecx; + mov output, edx; + } + return output; +} +#else +#warning using generic count leading zeroes. You may wish to write one for your CPU / compiler +static int count_leading_zeros(int input) +{ + int output = 0; + int curbyte = 0; + + curbyte = input >> 24; + if (curbyte) goto found; + output += 8; + + curbyte = input >> 16; + if (curbyte & 0xff) goto found; + output += 8; + + curbyte = input >> 8; + if (curbyte & 0xff) goto found; + output += 8; + + curbyte = input; + if (curbyte & 0xff) goto found; + output += 8; + + return output; + +found: + if (!(curbyte & 0xf0)) + { + output += 4; + } + else + curbyte >>= 4; + + if (curbyte & 0x8) + return output; + if (curbyte & 0x4) + return output + 1; + if (curbyte & 0x2) + return output + 2; + if (curbyte & 0x1) + return output + 3; + + /* shouldn't get here: */ + return output + 4; +} +#endif + +#define RICE_THRESHOLD 8 // maximum number of bits for a rice prefix. + +int32_t entropy_decode_value(alac_file* alac, + int readSampleSize, + int k, + int rice_kmodifier_mask) +{ + int32_t x = 0; // decoded value + + // read x, number of 1s before 0 represent the rice value. + while (x <= RICE_THRESHOLD && readbit(alac)) + { + x++; + } + + if (x > RICE_THRESHOLD) + { + // read the number from the bit stream (raw value) + int32_t value; + + value = readbits(alac, readSampleSize); + + // mask value + value &= (((uint32_t)0xffffffff) >> (32 - readSampleSize)); + + x = value; + } + else + { + if (k != 1) + { + int extraBits = readbits(alac, k); + + // x = x * (2^k - 1) + x *= (((1 << k) - 1) & rice_kmodifier_mask); + + if (extraBits > 1) + x += extraBits - 1; + else + unreadbits(alac, 1); + } + } + + return x; +} + +void entropy_rice_decode(alac_file* alac, + int32_t* outputBuffer, + int outputSize, + int readSampleSize, + int rice_initialhistory, + int rice_kmodifier, + int rice_historymult, + int rice_kmodifier_mask) +{ + int outputCount; + int history = rice_initialhistory; + int signModifier = 0; + + for (outputCount = 0; outputCount < outputSize; outputCount++) + { + int32_t decodedValue; + int32_t finalValue; + int32_t k; + + k = 31 - rice_kmodifier - count_leading_zeros((history >> 9) + 3); + + if (k < 0) k += rice_kmodifier; + else k = rice_kmodifier; + + // note: don't use rice_kmodifier_mask here (set mask to 0xFFFFFFFF) + decodedValue = entropy_decode_value(alac, readSampleSize, k, 0xFFFFFFFF); + + decodedValue += signModifier; + finalValue = (decodedValue + 1) / 2; // inc by 1 and shift out sign bit + if (decodedValue & 1) // the sign is stored in the low bit + finalValue *= -1; + + outputBuffer[outputCount] = finalValue; + + signModifier = 0; + + // update history + history += (decodedValue * rice_historymult) + - ((history * rice_historymult) >> 9); + + if (decodedValue > 0xFFFF) + history = 0xFFFF; + + // special case, for compressed blocks of 0 + if ((history < 128) && (outputCount + 1 < outputSize)) + { + int32_t blockSize; + + signModifier = 1; + + k = count_leading_zeros(history) + ((history + 16) / 64) - 24; + + // note: blockSize is always 16bit + blockSize = entropy_decode_value(alac, 16, k, rice_kmodifier_mask); + + // got blockSize 0s + if (blockSize > 0) + { + memset(&outputBuffer[outputCount + 1], 0, blockSize * sizeof(*outputBuffer)); + outputCount += blockSize; + } + + if (blockSize > 0xFFFF) + signModifier = 0; + + history = 0; + } + } +} + +#define SIGN_EXTENDED32(val, bits) ((val << (32 - bits)) >> (32 - bits)) + +#define SIGN_ONLY(v) \ + ((v < 0) ? (-1) : \ + ((v > 0) ? (1) : \ + (0))) + +static void predictor_decompress_fir_adapt(int32_t *error_buffer, + int32_t *buffer_out, + int output_size, + int readsamplesize, + int16_t *predictor_coef_table, + int predictor_coef_num, + int predictor_quantitization) +{ + int i; + + /* first sample always copies */ + *buffer_out = *error_buffer; + + if (!predictor_coef_num) + { + if (output_size <= 1) return; + memcpy(buffer_out+1, error_buffer+1, (output_size-1) * 4); + return; + } + + if (predictor_coef_num == 0x1f) /* 11111 - max value of predictor_coef_num */ + { /* second-best case scenario for fir decompression, + * error describes a small difference from the previous sample only + */ + if (output_size <= 1) return; + for (i = 0; i < output_size - 1; i++) + { + int32_t prev_value; + int32_t error_value; + + prev_value = buffer_out[i]; + error_value = error_buffer[i+1]; + buffer_out[i+1] = SIGN_EXTENDED32((prev_value + error_value), readsamplesize); + } + return; + } + + /* read warm-up samples */ + if (predictor_coef_num > 0) + { + int i; + for (i = 0; i < predictor_coef_num; i++) + { + int32_t val; + + val = buffer_out[i] + error_buffer[i+1]; + + val = SIGN_EXTENDED32(val, readsamplesize); + + buffer_out[i+1] = val; + } + } + +#if 0 + /* 4 and 8 are very common cases (the only ones i've seen). these + * should be unrolled and optimised + */ + if (predictor_coef_num == 4) + { + /* FIXME: optimised general case */ + return; + } + + if (predictor_coef_table == 8) + { + /* FIXME: optimised general case */ + return; + } +#endif + + + /* general case */ + if (predictor_coef_num > 0) + { + for (i = predictor_coef_num + 1; + i < output_size; + i++) + { + int j; + int sum = 0; + int outval; + int error_val = error_buffer[i]; + + for (j = 0; j < predictor_coef_num; j++) + { + sum += (buffer_out[predictor_coef_num-j] - buffer_out[0]) * + predictor_coef_table[j]; + } + + outval = (1 << (predictor_quantitization-1)) + sum; + outval = outval >> predictor_quantitization; + outval = outval + buffer_out[0] + error_val; + outval = SIGN_EXTENDED32(outval, readsamplesize); + + buffer_out[predictor_coef_num+1] = outval; + + if (error_val > 0) + { + int predictor_num = predictor_coef_num - 1; + + while (predictor_num >= 0 && error_val > 0) + { + int val = buffer_out[0] - buffer_out[predictor_coef_num - predictor_num]; + int sign = SIGN_ONLY(val); + + predictor_coef_table[predictor_num] -= sign; + + val *= sign; /* absolute value */ + + error_val -= ((val >> predictor_quantitization) * + (predictor_coef_num - predictor_num)); + + predictor_num--; + } + } + else if (error_val < 0) + { + int predictor_num = predictor_coef_num - 1; + + while (predictor_num >= 0 && error_val < 0) + { + int val = buffer_out[0] - buffer_out[predictor_coef_num - predictor_num]; + int sign = - SIGN_ONLY(val); + + predictor_coef_table[predictor_num] -= sign; + + val *= sign; /* neg value */ + + error_val -= ((val >> predictor_quantitization) * + (predictor_coef_num - predictor_num)); + + predictor_num--; + } + } + + buffer_out++; + } + } +} + +void deinterlace_16(int32_t *buffer_a, int32_t *buffer_b, + int16_t *buffer_out, + int numchannels, int numsamples, + uint8_t interlacing_shift, + uint8_t interlacing_leftweight) +{ + int i; + if (numsamples <= 0) return; + + /* weighted interlacing */ + if (interlacing_leftweight) + { + for (i = 0; i < numsamples; i++) + { + int32_t difference, midright; + int16_t left; + int16_t right; + + midright = buffer_a[i]; + difference = buffer_b[i]; + + + right = midright - ((difference * interlacing_leftweight) >> interlacing_shift); + left = right + difference; + + /* output is always little endian */ + if (host_bigendian) + { + _Swap16(left); + _Swap16(right); + } + + buffer_out[i*numchannels] = left; + buffer_out[i*numchannels + 1] = right; + } + + return; + } + + /* otherwise basic interlacing took place */ + for (i = 0; i < numsamples; i++) + { + int16_t left, right; + + left = buffer_a[i]; + right = buffer_b[i]; + + /* output is always little endian */ + if (host_bigendian) + { + _Swap16(left); + _Swap16(right); + } + + buffer_out[i*numchannels] = left; + buffer_out[i*numchannels + 1] = right; + } +} + +void deinterlace_24(int32_t *buffer_a, int32_t *buffer_b, + int uncompressed_bytes, + int32_t *uncompressed_bytes_buffer_a, int32_t *uncompressed_bytes_buffer_b, + void *buffer_out, + int numchannels, int numsamples, + uint8_t interlacing_shift, + uint8_t interlacing_leftweight) +{ + int i; + if (numsamples <= 0) return; + + /* weighted interlacing */ + if (interlacing_leftweight) + { + for (i = 0; i < numsamples; i++) + { + int32_t difference, midright; + int32_t left; + int32_t right; + + midright = buffer_a[i]; + difference = buffer_b[i]; + + right = midright - ((difference * interlacing_leftweight) >> interlacing_shift); + left = right + difference; + + if (uncompressed_bytes) + { + uint32_t mask = ~(0xFFFFFFFF << (uncompressed_bytes * 8)); + left <<= (uncompressed_bytes * 8); + right <<= (uncompressed_bytes * 8); + + left |= uncompressed_bytes_buffer_a[i] & mask; + right |= uncompressed_bytes_buffer_b[i] & mask; + } + + ((uint8_t*)buffer_out)[i * numchannels * 3] = (left) & 0xFF; + ((uint8_t*)buffer_out)[i * numchannels * 3 + 1] = (left >> 8) & 0xFF; + ((uint8_t*)buffer_out)[i * numchannels * 3 + 2] = (left >> 16) & 0xFF; + + ((uint8_t*)buffer_out)[i * numchannels * 3 + 3] = (right) & 0xFF; + ((uint8_t*)buffer_out)[i * numchannels * 3 + 4] = (right >> 8) & 0xFF; + ((uint8_t*)buffer_out)[i * numchannels * 3 + 5] = (right >> 16) & 0xFF; + } + + return; + } + + /* otherwise basic interlacing took place */ + for (i = 0; i < numsamples; i++) + { + int32_t left, right; + + left = buffer_a[i]; + right = buffer_b[i]; + + if (uncompressed_bytes) + { + uint32_t mask = ~(0xFFFFFFFF << (uncompressed_bytes * 8)); + left <<= (uncompressed_bytes * 8); + right <<= (uncompressed_bytes * 8); + + left |= uncompressed_bytes_buffer_a[i] & mask; + right |= uncompressed_bytes_buffer_b[i] & mask; + } + + ((uint8_t*)buffer_out)[i * numchannels * 3] = (left) & 0xFF; + ((uint8_t*)buffer_out)[i * numchannels * 3 + 1] = (left >> 8) & 0xFF; + ((uint8_t*)buffer_out)[i * numchannels * 3 + 2] = (left >> 16) & 0xFF; + + ((uint8_t*)buffer_out)[i * numchannels * 3 + 3] = (right) & 0xFF; + ((uint8_t*)buffer_out)[i * numchannels * 3 + 4] = (right >> 8) & 0xFF; + ((uint8_t*)buffer_out)[i * numchannels * 3 + 5] = (right >> 16) & 0xFF; + + } + +} + +void decode_frame(alac_file *alac, + unsigned char *inbuffer, + void *outbuffer, int *outputsize) +{ + int channels; + int32_t outputsamples = alac->setinfo_max_samples_per_frame; + + /* setup the stream */ + alac->input_buffer = inbuffer; + alac->input_buffer_bitaccumulator = 0; + + channels = readbits(alac, 3); + + *outputsize = outputsamples * alac->bytespersample; + + switch(channels) + { + case 0: /* 1 channel */ + { + int hassize; + int isnotcompressed; + int readsamplesize; + + int uncompressed_bytes; + int ricemodifier; + + /* 2^result = something to do with output waiting. + * perhaps matters if we read > 1 frame in a pass? + */ + readbits(alac, 4); + + readbits(alac, 12); /* unknown, skip 12 bits */ + + hassize = readbits(alac, 1); /* the output sample size is stored soon */ + + uncompressed_bytes = readbits(alac, 2); /* number of bytes in the (compressed) stream that are not compressed */ + + isnotcompressed = readbits(alac, 1); /* whether the frame is compressed */ + + if (hassize) + { + /* now read the number of samples, + * as a 32bit integer */ + outputsamples = readbits(alac, 32); + *outputsize = outputsamples * alac->bytespersample; + } + + readsamplesize = alac->setinfo_sample_size - (uncompressed_bytes * 8); + + if (!isnotcompressed) + { /* so it is compressed */ + int16_t predictor_coef_table[32]; + int predictor_coef_num; + int prediction_type; + int prediction_quantitization; + int i; + + /* skip 16 bits, not sure what they are. seem to be used in + * two channel case */ + readbits(alac, 8); + readbits(alac, 8); + + prediction_type = readbits(alac, 4); + prediction_quantitization = readbits(alac, 4); + + ricemodifier = readbits(alac, 3); + predictor_coef_num = readbits(alac, 5); + + /* read the predictor table */ + for (i = 0; i < predictor_coef_num; i++) + { + predictor_coef_table[i] = (int16_t)readbits(alac, 16); + } + + if (uncompressed_bytes) + { + int i; + for (i = 0; i < outputsamples; i++) + { + alac->uncompressed_bytes_buffer_a[i] = readbits(alac, uncompressed_bytes * 8); + } + } + + entropy_rice_decode(alac, + alac->predicterror_buffer_a, + outputsamples, + readsamplesize, + alac->setinfo_rice_initialhistory, + alac->setinfo_rice_kmodifier, + ricemodifier * alac->setinfo_rice_historymult / 4, + (1 << alac->setinfo_rice_kmodifier) - 1); + + if (prediction_type == 0) + { /* adaptive fir */ + predictor_decompress_fir_adapt(alac->predicterror_buffer_a, + alac->outputsamples_buffer_a, + outputsamples, + readsamplesize, + predictor_coef_table, + predictor_coef_num, + prediction_quantitization); + } + else + { + fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type); + /* i think the only other prediction type (or perhaps this is just a + * boolean?) runs adaptive fir twice.. like: + * predictor_decompress_fir_adapt(predictor_error, tempout, ...) + * predictor_decompress_fir_adapt(predictor_error, outputsamples ...) + * little strange.. + */ + } + + } + else + { /* not compressed, easy case */ + if (alac->setinfo_sample_size <= 16) + { + int i; + for (i = 0; i < outputsamples; i++) + { + int32_t audiobits = readbits(alac, alac->setinfo_sample_size); + + audiobits = SIGN_EXTENDED32(audiobits, alac->setinfo_sample_size); + + alac->outputsamples_buffer_a[i] = audiobits; + } + } + else + { + int i; + for (i = 0; i < outputsamples; i++) + { + int32_t audiobits; + + audiobits = readbits(alac, 16); + /* special case of sign extension.. + * as we'll be ORing the low 16bits into this */ + audiobits = audiobits << (alac->setinfo_sample_size - 16); + audiobits |= readbits(alac, alac->setinfo_sample_size - 16); + audiobits = SignExtend24(audiobits); + + alac->outputsamples_buffer_a[i] = audiobits; + } + } + uncompressed_bytes = 0; // always 0 for uncompressed + } + + switch(alac->setinfo_sample_size) + { + case 16: + { + int i; + for (i = 0; i < outputsamples; i++) + { + int16_t sample = alac->outputsamples_buffer_a[i]; + if (host_bigendian) + _Swap16(sample); + ((int16_t*)outbuffer)[i * alac->numchannels] = sample; + } + break; + } + case 24: + { + int i; + for (i = 0; i < outputsamples; i++) + { + int32_t sample = alac->outputsamples_buffer_a[i]; + + if (uncompressed_bytes) + { + uint32_t mask; + sample = sample << (uncompressed_bytes * 8); + mask = ~(0xFFFFFFFF << (uncompressed_bytes * 8)); + sample |= alac->uncompressed_bytes_buffer_a[i] & mask; + } + + ((uint8_t*)outbuffer)[i * alac->numchannels * 3] = (sample) & 0xFF; + ((uint8_t*)outbuffer)[i * alac->numchannels * 3 + 1] = (sample >> 8) & 0xFF; + ((uint8_t*)outbuffer)[i * alac->numchannels * 3 + 2] = (sample >> 16) & 0xFF; + } + break; + } + case 20: + case 32: + fprintf(stderr, "FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size); + break; + default: + break; + } + break; + } + case 1: /* 2 channels */ + { + int hassize; + int isnotcompressed; + int readsamplesize; + + int uncompressed_bytes; + + uint8_t interlacing_shift; + uint8_t interlacing_leftweight; + + /* 2^result = something to do with output waiting. + * perhaps matters if we read > 1 frame in a pass? + */ + readbits(alac, 4); + + readbits(alac, 12); /* unknown, skip 12 bits */ + + hassize = readbits(alac, 1); /* the output sample size is stored soon */ + + uncompressed_bytes = readbits(alac, 2); /* the number of bytes in the (compressed) stream that are not compressed */ + + isnotcompressed = readbits(alac, 1); /* whether the frame is compressed */ + + if (hassize) + { + /* now read the number of samples, + * as a 32bit integer */ + outputsamples = readbits(alac, 32); + *outputsize = outputsamples * alac->bytespersample; + } + + readsamplesize = alac->setinfo_sample_size - (uncompressed_bytes * 8) + 1; + + if (!isnotcompressed) + { /* compressed */ + int16_t predictor_coef_table_a[32]; + int predictor_coef_num_a; + int prediction_type_a; + int prediction_quantitization_a; + int ricemodifier_a; + + int16_t predictor_coef_table_b[32]; + int predictor_coef_num_b; + int prediction_type_b; + int prediction_quantitization_b; + int ricemodifier_b; + + int i; + + interlacing_shift = readbits(alac, 8); + interlacing_leftweight = readbits(alac, 8); + + /******** channel 1 ***********/ + prediction_type_a = readbits(alac, 4); + prediction_quantitization_a = readbits(alac, 4); + + ricemodifier_a = readbits(alac, 3); + predictor_coef_num_a = readbits(alac, 5); + + /* read the predictor table */ + for (i = 0; i < predictor_coef_num_a; i++) + { + predictor_coef_table_a[i] = (int16_t)readbits(alac, 16); + } + + /******** channel 2 *********/ + prediction_type_b = readbits(alac, 4); + prediction_quantitization_b = readbits(alac, 4); + + ricemodifier_b = readbits(alac, 3); + predictor_coef_num_b = readbits(alac, 5); + + /* read the predictor table */ + for (i = 0; i < predictor_coef_num_b; i++) + { + predictor_coef_table_b[i] = (int16_t)readbits(alac, 16); + } + + /*********************/ + if (uncompressed_bytes) + { /* see mono case */ + int i; + for (i = 0; i < outputsamples; i++) + { + alac->uncompressed_bytes_buffer_a[i] = readbits(alac, uncompressed_bytes * 8); + alac->uncompressed_bytes_buffer_b[i] = readbits(alac, uncompressed_bytes * 8); + } + } + + /* channel 1 */ + entropy_rice_decode(alac, + alac->predicterror_buffer_a, + outputsamples, + readsamplesize, + alac->setinfo_rice_initialhistory, + alac->setinfo_rice_kmodifier, + ricemodifier_a * alac->setinfo_rice_historymult / 4, + (1 << alac->setinfo_rice_kmodifier) - 1); + + if (prediction_type_a == 0) + { /* adaptive fir */ + predictor_decompress_fir_adapt(alac->predicterror_buffer_a, + alac->outputsamples_buffer_a, + outputsamples, + readsamplesize, + predictor_coef_table_a, + predictor_coef_num_a, + prediction_quantitization_a); + } + else + { /* see mono case */ + fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type_a); + } + + /* channel 2 */ + entropy_rice_decode(alac, + alac->predicterror_buffer_b, + outputsamples, + readsamplesize, + alac->setinfo_rice_initialhistory, + alac->setinfo_rice_kmodifier, + ricemodifier_b * alac->setinfo_rice_historymult / 4, + (1 << alac->setinfo_rice_kmodifier) - 1); + + if (prediction_type_b == 0) + { /* adaptive fir */ + predictor_decompress_fir_adapt(alac->predicterror_buffer_b, + alac->outputsamples_buffer_b, + outputsamples, + readsamplesize, + predictor_coef_table_b, + predictor_coef_num_b, + prediction_quantitization_b); + } + else + { + fprintf(stderr, "FIXME: unhandled predicition type: %i\n", prediction_type_b); + } + } + else + { /* not compressed, easy case */ + if (alac->setinfo_sample_size <= 16) + { + int i; + for (i = 0; i < outputsamples; i++) + { + int32_t audiobits_a, audiobits_b; + + audiobits_a = readbits(alac, alac->setinfo_sample_size); + audiobits_b = readbits(alac, alac->setinfo_sample_size); + + audiobits_a = SIGN_EXTENDED32(audiobits_a, alac->setinfo_sample_size); + audiobits_b = SIGN_EXTENDED32(audiobits_b, alac->setinfo_sample_size); + + alac->outputsamples_buffer_a[i] = audiobits_a; + alac->outputsamples_buffer_b[i] = audiobits_b; + } + } + else + { + int i; + for (i = 0; i < outputsamples; i++) + { + int32_t audiobits_a, audiobits_b; + + audiobits_a = readbits(alac, 16); + audiobits_a = audiobits_a << (alac->setinfo_sample_size - 16); + audiobits_a |= readbits(alac, alac->setinfo_sample_size - 16); + audiobits_a = SignExtend24(audiobits_a); + + audiobits_b = readbits(alac, 16); + audiobits_b = audiobits_b << (alac->setinfo_sample_size - 16); + audiobits_b |= readbits(alac, alac->setinfo_sample_size - 16); + audiobits_b = SignExtend24(audiobits_b); + + alac->outputsamples_buffer_a[i] = audiobits_a; + alac->outputsamples_buffer_b[i] = audiobits_b; + } + } + uncompressed_bytes = 0; // always 0 for uncompressed + interlacing_shift = 0; + interlacing_leftweight = 0; + } + + switch(alac->setinfo_sample_size) + { + case 16: + { + deinterlace_16(alac->outputsamples_buffer_a, + alac->outputsamples_buffer_b, + (int16_t*)outbuffer, + alac->numchannels, + outputsamples, + interlacing_shift, + interlacing_leftweight); + break; + } + case 24: + { + deinterlace_24(alac->outputsamples_buffer_a, + alac->outputsamples_buffer_b, + uncompressed_bytes, + alac->uncompressed_bytes_buffer_a, + alac->uncompressed_bytes_buffer_b, + (int16_t*)outbuffer, + alac->numchannels, + outputsamples, + interlacing_shift, + interlacing_leftweight); + break; + } + case 20: + case 32: + fprintf(stderr, "FIXME: unimplemented sample size %i\n", alac->setinfo_sample_size); + break; + default: + break; + } + + break; + } + } +} + +alac_file *create_alac(int samplesize, int numchannels) +{ + alac_file *newfile = malloc(sizeof(alac_file)); + + newfile->samplesize = samplesize; + newfile->numchannels = numchannels; + newfile->bytespersample = (samplesize / 8) * numchannels; + + return newfile; +} diff --git a/alac.h b/alac.h new file mode 100644 index 00000000..17bd8b9d --- /dev/null +++ b/alac.h @@ -0,0 +1,54 @@ +#ifndef __ALAC__DECOMP_H +#define __ALAC__DECOMP_H + +typedef struct alac_file alac_file; + +alac_file *create_alac(int samplesize, int numchannels); +void decode_frame(alac_file *alac, + unsigned char *inbuffer, + void *outbuffer, int *outputsize); +void alac_set_info(alac_file *alac, char *inputbuffer); +void allocate_buffers(alac_file *alac); + +struct alac_file +{ + unsigned char *input_buffer; + int input_buffer_bitaccumulator; /* used so we can do arbitary + bit reads */ + + int samplesize; + int numchannels; + int bytespersample; + + + /* buffers */ + int32_t *predicterror_buffer_a; + int32_t *predicterror_buffer_b; + + int32_t *outputsamples_buffer_a; + int32_t *outputsamples_buffer_b; + + int32_t *uncompressed_bytes_buffer_a; + int32_t *uncompressed_bytes_buffer_b; + + + + /* stuff from setinfo */ + uint32_t setinfo_max_samples_per_frame; /* 0x1000 = 4096 */ /* max samples per frame? */ + uint8_t setinfo_7a; /* 0x00 */ + uint8_t setinfo_sample_size; /* 0x10 */ + uint8_t setinfo_rice_historymult; /* 0x28 */ + uint8_t setinfo_rice_initialhistory; /* 0x0a */ + uint8_t setinfo_rice_kmodifier; /* 0x0e */ + uint8_t setinfo_7f; /* 0x02 */ + uint16_t setinfo_80; /* 0x00ff */ + uint32_t setinfo_82; /* 0x000020e7 */ /* max sample size?? */ + uint32_t setinfo_86; /* 0x00069fe4 */ /* bit rate (avarge)?? */ + uint32_t setinfo_8a_rate; /* 0x0000ac44 */ + /* end setinfo stuff */ + +}; + + +#endif /* __ALAC__DECOMP_H */ + diff --git a/hairtunes.c b/hairtunes.c new file mode 100644 index 00000000..aa5f08ea --- /dev/null +++ b/hairtunes.c @@ -0,0 +1,656 @@ +/* + * HairTunes - RAOP packet handler and slave-clocked replay engine + * Copyright (c) James Laird 2011 + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef FANCY_RESAMPLING +#include +#endif + +#include +int debug = 0; + +#include "alac.h" + +// default buffer - about half a second +#define BUFFER_FRAMES 64 +#define START_FILL 55 +#define MAX_PACKET 2048 + +typedef unsigned short seq_t; + +// global options (constant after init) +unsigned char aeskey[16], aesiv[16]; +AES_KEY aes; +char *rtphost = 0; +int dataport = 0, controlport = 0, timingport = 0; +int fmtp[32]; +int sampling_rate; +int frame_size; +#define FRAME_BYTES (4*frame_size) +// maximal resampling shift - conservative +#define OUTFRAME_BYTES (4*(frame_size+3)) + + +alac_file *decoder_info; + +#ifdef FANCY_RESAMPLING +int fancy_resampling = 1; +SRC_STATE *src; +#endif + +void rtp_request_resend(seq_t first, seq_t last); +void init_buffer(void); +void ab_resync(void); + +// interthread variables + // stdin->decoder +volatile double volume = 1.0; +volatile long fix_volume = 0x10000; + +typedef struct audio_buffer_entry { // decoded audio packets + int ready; + signed short *data; +} abuf_t; +volatile abuf_t audio_buffer[BUFFER_FRAMES]; +#define BUFIDX(seqno) ((seq_t)(seqno) % BUFFER_FRAMES) + +// mutex-protected variables +volatile seq_t ab_read, ab_write; +int ab_buffering = 1, ab_synced = 0; +pthread_mutex_t ab_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t ab_buffer_ready = PTHREAD_COND_INITIALIZER; + +void die(char *why) { + fprintf(stderr, "FATAL: %s\n", why); + exit(1); +} + +int hex2bin(unsigned char *buf, char *hex) { + int i, j; + if (strlen(hex) != 0x20) + return 1; + for (i=0; i<0x10; i++) { + if (!sscanf(hex, "%2X", &j)) + return 1; + hex += 2; + *buf++ = j; + } + return 0; +} + +int init_decoder(void) { + alac_file *alac; + + frame_size = fmtp[1]; // stereo samples + sampling_rate = fmtp[11]; + + int sample_size = fmtp[3]; + if (sample_size != 16) + die("only 16-bit samples supported!"); + + alac = create_alac(sample_size, 2); + if (!alac) + return 1; + decoder_info = alac; + + alac->setinfo_max_samples_per_frame = frame_size; + alac->setinfo_7a = fmtp[2]; + alac->setinfo_sample_size = sample_size; + alac->setinfo_rice_historymult = fmtp[4]; + alac->setinfo_rice_initialhistory = fmtp[5]; + alac->setinfo_rice_kmodifier = fmtp[6]; + alac->setinfo_7f = fmtp[7]; + alac->setinfo_80 = fmtp[8]; + alac->setinfo_82 = fmtp[9]; + alac->setinfo_86 = fmtp[10]; + alac->setinfo_8a_rate = fmtp[11]; + allocate_buffers(alac); + return 0; +} + +int main(int argc, char **argv) { + char *hexaeskey = 0, *hexaesiv = 0; + char *fmtpstr = 0; + char *arg; + int i; + assert(RAND_MAX >= 0x10000); // XXX move this to compile time + while (arg = *++argv) { + if (!strcasecmp(arg, "iv")) { + hexaesiv = *++argv; + argc--; + } else + if (!strcasecmp(arg, "key")) { + hexaeskey = *++argv; + argc--; + } else + if (!strcasecmp(arg, "fmtp")) { + fmtpstr = *++argv; + } else + if (!strcasecmp(arg, "cport")) { + controlport = atoi(*++argv); + } else + if (!strcasecmp(arg, "tport")) { + timingport = atoi(*++argv); + } else + if (!strcasecmp(arg, "dport")) { + dataport = atoi(*++argv); + } else + if (!strcasecmp(arg, "host")) { + rtphost = *++argv; + } +#ifdef FANCY_RESAMPLING + else + if (!strcasecmp(arg, "resamp")) { + fancy_resampling = atoi(*++argv); + } +#endif + } + + if (!hexaeskey || !hexaesiv) + die("Must supply AES key and IV!"); + + if (hex2bin(aesiv, hexaesiv)) + die("can't understand IV"); + if (hex2bin(aeskey, hexaeskey)) + die("can't understand key"); + AES_set_decrypt_key(aeskey, 128, &aes); + + memset(fmtp, 0, sizeof(fmtp)); + i = 0; + while (arg = strsep(&fmtpstr, " \t")) + fmtp[i++] = atoi(arg); + + init_decoder(); + init_buffer(); + init_rtp(); // open a UDP listen port and start a listener; decode into ring buffer + fflush(stdout); + init_output(); // resample and output from ring buffer + + char line[128]; + int in_line = 0; + int n; + double f; + while (fgets(line + in_line, sizeof(line) - in_line, stdin)) { + n = strlen(line); + if (line[n-1] != '\n') { + in_line = strlen(line) - 1; + if (n == sizeof(line)-1) + in_line = 0; + continue; + } + if (sscanf(line, "vol: %lf\n", &f)) { + assert(f<=0); + if (debug) + fprintf(stderr, "VOL: %lf\n", f); + volume = pow(10.0,0.1*f); + fix_volume = 65536.0 * volume; + continue; + } + if (!strcmp(line, "exit\n")) { + exit(0); + } + if (!strcmp(line, "flush\n")) { + pthread_mutex_lock(&ab_mutex); + ab_resync(); + pthread_mutex_unlock(&ab_mutex); + if (debug) + fprintf(stderr, "FLUSH\n"); + } + } + fprintf(stderr, "bye!\n"); + fflush(stderr); +} + +void init_buffer(void) { + int i; + for (i=0; i 0; +} + +void alac_decode(short *dest, char *buf, int len) { + char packet[MAX_PACKET]; + assert(len<=MAX_PACKET); + + char iv[16]; + int i; + memcpy(iv, aesiv, sizeof(iv)); + for (i=0; i+16<=len; i += 16) + AES_cbc_encrypt(buf+i, packet+i, 0x10, &aes, iv, AES_DECRYPT); + if (len & 0xf) + memcpy(packet+i, buf+i, len & 0xf); + + int outsize; + + decode_frame(decoder_info, packet, dest, &outsize); + + assert(outsize == FRAME_BYTES); +} + +void buffer_put_packet(seq_t seqno, char *data, int len) { + volatile abuf_t *abuf = 0; + short read; + short buf_fill; + + pthread_mutex_lock(&ab_mutex); + if (!ab_synced) { + ab_write = seqno; + ab_read = seqno-1; + ab_synced = 1; + } + if (seqno == ab_write+1) { // expected packet + abuf = audio_buffer + BUFIDX(seqno); + ab_write = seqno; + } else if (seq_order(ab_write, seqno)) { // newer than expected + rtp_request_resend(ab_write, seqno-1); + abuf = audio_buffer + BUFIDX(seqno); + ab_write = seqno; + } else if (seq_order(ab_read, seqno)) { // late but not yet played + abuf = audio_buffer + BUFIDX(seqno); + } else { // too late. + fprintf(stderr, "\nlate packet %04X (%04X:%04X)\n", seqno, ab_read, ab_write); + } + buf_fill = ab_write - ab_read; + pthread_mutex_unlock(&ab_mutex); + + if (abuf) { + alac_decode(abuf->data, data, len); + abuf->ready = 1; + } + + if (ab_buffering && buf_fill >= START_FILL) + pthread_cond_signal(&ab_buffer_ready); + if (!ab_buffering) { + // check if the t+10th packet has arrived... last-chance resend + read = ab_read + 10; + abuf = audio_buffer + BUFIDX(read); + if (!abuf->ready) + rtp_request_resend(read, read); + } +} + +static int rtp_sockets[2]; // data, control +struct sockaddr_in rtp_client; +void *rtp_thread_func(void *arg) { + socklen_t si_len = sizeof(rtp_client); + char packet[MAX_PACKET]; + char *pktp; + seq_t seqno; + ssize_t plen; + int sock = rtp_sockets[0], csock = rtp_sockets[1]; + int readsock; + char type; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + FD_SET(csock, &fds); + + while (select(csock>sock ? csock+1 : sock+1, &fds, 0, 0, 0)!=-1) { + if (FD_ISSET(sock, &fds)) { + readsock = sock; + } else { + readsock = csock; + } + FD_SET(sock, &fds); + FD_SET(csock, &fds); + + plen = recvfrom(readsock, packet, sizeof(packet), 0, (struct sockaddr*)&rtp_client, &si_len); + if (plen < 0) + continue; + assert(plen<=MAX_PACKET); + + type = packet[1] & ~0x80; + if (type == 0x60 || type == 0x56) { // audio data / resend + pktp = packet; + if (type==0x56) { + pktp += 4; + plen -= 4; + } + seqno = ntohs(*(unsigned short *)(pktp+2)); + buffer_put_packet(seqno, pktp+12, plen-12); + } + } +} + +void rtp_request_resend(seq_t first, seq_t last) { + if (seq_order(last, first)) + return; + + fprintf(stderr, "requesting resend on %d packets (port %d)\n", last-first+1, controlport); + + char req[8]; // *not* a standard RTCP NACK + req[0] = 0x80; + req[1] = 0x55|0x80; // Apple 'resend' + *(unsigned short *)(req+2) = htons(1); // our seqnum + *(unsigned short *)(req+4) = htons(first); // missed seqnum + *(unsigned short *)(req+6) = htons(last-first+1); // count + + rtp_client.sin_port = htons(controlport); + sendto(rtp_sockets[1], req, sizeof(req), 0, (struct sockaddr *)&rtp_client, sizeof(struct sockaddr_in)); +} + + +int init_rtp(void) { + struct sockaddr_in si; + int sock, csock; // data and control (we treat the streams the same here) + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock==-1) + die("Can't create socket!"); + + memset(&si, 0, sizeof(si)); + si.sin_family = AF_INET; + si.sin_addr.s_addr = htonl(INADDR_ANY); + + unsigned short port = 6000 - 3; + do { + port += 3; + si.sin_port = htons(port); + } while (bind(sock, (struct sockaddr*)&si, sizeof(si))==-1); + + csock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (csock==-1) + die("Can't create socket!"); + si.sin_port = htons(port + 1); + if (bind(csock, (struct sockaddr*)&si, sizeof(si))==-1) + die("can't bind control socket"); + + printf("port: %d\n", port); // let our handler know where we end up listening + printf("cport: %d\n", port+1); + + pthread_t rtp_thread; + rtp_sockets[0] = sock; + rtp_sockets[1] = csock; + pthread_create(&rtp_thread, NULL, rtp_thread_func, (void *)rtp_sockets); + + return port; +} + +static inline short dithered_vol(short sample) { + static short rand_a, rand_b; + long out; + rand_b = rand_a; + rand_a = rand() & 0xffff; + + out = (long)sample * fix_volume; + if (fix_volume < 0x10000) { + out += rand_a; + out -= rand_b; + } + return out>>16; +} + +typedef struct { + double hist[2]; + double a[2]; + double b[3]; +} biquad_t; + +static void biquad_init(biquad_t *bq, double a[], double b[]) { + bq->hist[0] = bq->hist[1] = 0.0; + memcpy(bq->a, a, 2*sizeof(double)); + memcpy(bq->b, b, 3*sizeof(double)); +} + +static void biquad_lpf(biquad_t *bq, double freq, double Q) { + double w0 = 2*M_PI*freq/((float)sampling_rate/(float)frame_size); + double alpha = sin(w0)/(2.0*Q); + + double a_0 = 1.0 + alpha; + double b[3], a[2]; + b[0] = (1.0-cos(w0))/(2.0*a_0); + b[1] = (1.0-cos(w0))/a_0; + b[2] = b[0]; + a[0] = -2.0*cos(w0)/a_0; + a[1] = (1-alpha)/a_0; + + biquad_init(bq, a, b); +} + +static double biquad_filt(biquad_t *bq, double in) { + double w = in - bq->a[0]*bq->hist[0] - bq->a[1]*bq->hist[1]; + double out = bq->b[1]*bq->hist[0] + bq->b[2]*bq->hist[1] + bq->b[0]*w; + bq->hist[1] = bq->hist[0]; + bq->hist[0] = w; +} + +double bf_playback_rate = 1.0; + +static double bf_est_drift = 0.0; // local clock is slower by +static biquad_t bf_drift_lpf; +static double bf_est_err = 0.0, bf_last_err; +static biquad_t bf_err_lpf, bf_err_deriv_lpf; +static double desired_fill; +static int fill_count; + +void bf_est_reset(short fill) { + biquad_lpf(&bf_drift_lpf, 1.0/180.0, 0.3); + biquad_lpf(&bf_err_lpf, 1.0/10.0, 0.25); + biquad_lpf(&bf_err_deriv_lpf, 1.0/2.0, 0.2); + fill_count = 0; + bf_playback_rate = 1.0; + bf_est_err = bf_last_err = 0; + desired_fill = fill_count = 0; +} +void bf_est_update(short fill) { + if (fill_count < 1000) { + desired_fill += (double)fill/1000.0; + fill_count++; + return; + } + +#define CONTROL_A (1e-4) +#define CONTROL_B (1e-1) + + double buf_delta = fill - desired_fill; + bf_est_err = biquad_filt(&bf_err_lpf, buf_delta); + double err_deriv = biquad_filt(&bf_err_deriv_lpf, bf_est_err - bf_last_err); + + bf_est_drift = biquad_filt(&bf_drift_lpf, CONTROL_B*(bf_est_err*CONTROL_A + err_deriv) + bf_est_drift); + + if (debug) + fprintf(stderr, "bf %d err %f drift %f desiring %f ed %f estd %f\r", fill, bf_est_err, bf_est_drift, desired_fill, err_deriv, err_deriv + CONTROL_A*bf_est_err); + bf_playback_rate = 1.0 + CONTROL_A*bf_est_err + bf_est_drift; + + bf_last_err = bf_est_err; +} + +// get the next frame, when available. return 0 if underrun/stream reset. +short *buffer_get_frame(void) { + short buf_fill; + seq_t read; + + pthread_mutex_lock(&ab_mutex); + + buf_fill = ab_write - ab_read; + if (buf_fill < 1 || !ab_synced) { // init or underrun. stop and wait + if (ab_synced) + fprintf(stderr, "\nunderrun.\n"); + + ab_buffering = 1; + pthread_cond_wait(&ab_buffer_ready, &ab_mutex); + ab_read++; + buf_fill = ab_write - ab_read; + pthread_mutex_unlock(&ab_mutex); + + bf_est_reset(buf_fill); + return 0; + } + if (buf_fill >= BUFFER_FRAMES) { // overrunning! uh-oh. restart at a sane distance + fprintf(stderr, "\noverrun.\n"); + ab_read = ab_write - START_FILL; + } + read = ab_read; + ab_read++; + pthread_mutex_unlock(&ab_mutex); + + buf_fill = ab_write - ab_read; + bf_est_update(buf_fill); + + volatile abuf_t *curframe = audio_buffer + BUFIDX(read); + if (!curframe->ready) { + fprintf(stderr, "\nmissing frame.\n"); + memset(curframe->data, 0, FRAME_BYTES); + } + curframe->ready = 0; + return curframe->data; +} + +int stuff_buffer(double playback_rate, short *inptr, short *outptr) { + int i; + int stuffsamp = frame_size; + int stuff = 0; + double p_stuff; + + p_stuff = 1.0 - pow(1.0 - fabs(playback_rate-1.0), frame_size); + + if ((float)rand()/((float)RAND_MAX) < p_stuff) { + stuff = playback_rate > 1.0 ? -1 : 1; + stuffsamp = rand() % (frame_size - 1); + } + + for (i=0; i> 1); + *outptr++ = dithered_vol(((long)inptr[-1] + (long)inptr[1]) >> 1); + } else if (stuff==-1) { + if (debug) + fprintf(stderr, "---------\n"); + inptr++; + inptr++; + } + for (i=stuffsamp; i{decoder_pid} } } keys %conns; + kill 9, $avahi_publish; + exit(0); +}; + +my $airport_pem = join '', ; +my $rsa = Crypt::OpenSSL::RSA->new_private_key($airport_pem) || die; + +my $listen = new IO::Socket::INET(Listen => 1, + LocalPort => 5000, + ReuseAddr => 1, + Proto => 'tcp'); +die "Can't listen on port 5000: $!" unless $listen; + +my $sel = new IO::Select($listen); + +print "listening...\n"; + +while (1) { + my @waiting = $sel->can_read; + foreach $fh (@waiting) { + if ($fh==$listen) { + my $new = $listen->accept; + print "new connection\n"; + $sel->add($new); + $new->blocking(0); + $conns{$new} = {fh => $fh}; + } else { + if (eof($fh)) { + print "closed: $fh\n"; + $sel->remove($fh); + close $fh; + eval { kill $conns{$fh}{decoder_pid} }; + delete $conns{$fh}; + next; + } + if (exists $conns{$fh}) { + conn_handle_data($fh); + } + } + } +} + +sub conn_handle_data { + my $fh = shift; + my $conn = $conns{$fh}; + + if ($conn->{req_need}) { + if (length($conn->{data}) >= $conn->{req_need}) { + $conn->{req}->content(substr($conn->{data}, 0, $conn->{req_need}, '')); + conn_handle_request($fh, $conn); + } + undef $conn->{req_need}; + return; + } + + read $fh, my $data, 4096; + $conn->{data} .= $data; + + if ($conn->{data} =~ /(\r\n\r\n|\n\n|\r\r)/) { + my $req_data = substr($conn->{data}, 0, $+[0], ''); + $conn->{req} = HTTP::Request->parse($req_data); + printf "REQ: %s\n", $conn->{req}->method; + conn_handle_request($fh, $conn); + conn_handle_data($fh) if length($conn->{data}); + } +} + +sub digest_ok { + my ($req, $conn) = @_; + my $authz = $req->header('Authorization'); + return 0 unless $authz =~ s/^Digest\s+//i; + return 0 unless length $conn->{nonce}; + my @authz = split /,\s*/, $authz; + my %authz = map { /(.+)="(.+)"/; ($1, $2) } @authz; + + # not a standard digest - uses capital hex digits, in conflict with the RFC + my $digest = uc md5_hex ( + uc(md5_hex($authz{username} . ':' . $authz{realm} . ':' . $password)) + . ':' . $authz{nonce} . ':' . + uc(md5_hex($req->method . ':' . $authz{uri})) + ); + + return $digest eq $authz{response}; +} + +sub conn_handle_request { + my ($fh, $conn) = @_; + + my $req = $conn->{req};; + my $clen = $req->header('content-length') // 0; + if ($clen > 0 && !length($req->content)) { + $conn->{req_need} = $clen; + return; # need more! + } + + my $resp = HTTP::Response->new(200); + $resp->request($req); + $resp->protocol($req->protocol); + + $resp->header('CSeq', $req->header('CSeq')); + $resp->header('Audio-Jack-Status', 'connected; type=analog'); + + if (my $chall = $req->header('Apple-Challenge')) { + my $data = decode_base64($chall); + $data .= join '', map { chr } split(/\./, $fh->sockhost); + $data .= join '', map { chr } @hw_addr; + $data .= chr(0) x (0x20-length($data)); + + $rsa->use_pkcs1_padding; # this isn't hashed before signing + my $signature = encode_base64 $rsa->private_encrypt($data), ''; + $signature =~ s/=*$//; + $resp->header('Apple-Response', $signature); + } + + if (length $password) { + if (!digest_ok($req, $conn)) { + my $nonce = md5_hex(map { rand } 1..20); + $conn->{nonce} = $nonce; + $resp->header('WWW-Authenticate', "Digest realm=\"$apname\", nonce=\"$nonce\""); + $resp->code(401); + $req->method('DENIED'); + } + } + + for ($req->method) { + /^OPTIONS$/ && do { + $resp->header('Public', 'ANNOUNCE, SETUP, RECORD, PAUSE, FLUSH, TEARDOWN, OPTIONS, GET_PARAMETER, SET_PARAMETER'); + last; + }; + + /^ANNOUNCE$/ && do { + my $sdptext = $req->content; + my @sdplines = split /[\r\n]+/, $sdptext; + my %sdp = map { ($1, $2) if /^a=([^:]+):(.+)/ } @sdplines; + die("no AESIV") unless my $aesiv = decode_base64($sdp{aesiv}); + die("no AESKEY") unless my $rsaaeskey = decode_base64($sdp{rsaaeskey}); + $rsa->use_pkcs1_oaep_padding; + my $aeskey = $rsa->decrypt($rsaaeskey) || die; + + $conn->{aesiv} = $aesiv; + $conn->{aeskey} = $aeskey; + $conn->{fmtp} = $sdp{fmtp}; + last; + }; + + /^SETUP$/ && do { + my $transport = $req->header('Transport'); + $transport =~ s/;control_port=(\d+)//; + my $cport = $1; + $transport =~ s/;timing_port=(\d+)//; + my $tport = $1; + $transport =~ s/;server_port=(\d+)//; + my $dport = $1; + $resp->header('Session', 'DEADBEEF'); + + + my $dec = sprintf("./hairtunes iv %s key %s fmtp %s cport %s tport %s dport %s host %s", + map { "'$_'" } ( + unpack('H*', $conn->{aesiv}), + unpack('H*', $conn->{aeskey}), + $conn->{fmtp}, + $cport, $tport, $dport, + 'unused' + )); + # print "decode command: $dec\n"; + my $decoder = open2(my $dec_out, my $dec_in, $dec); + + $conn->{decoder_pid} = $decoder; + $conn->{decoder_fh} = $dec_in; + my $portdesc = <$dec_out>; + die("Expected port number from decoder; got $portdesc") unless $portdesc =~ /^port: (\d+)/; + my $port = $1; + print "launched decoder: $decoder on port: $port\n"; + $resp->header('Transport', $req->header('Transport') . ";server_port=$port"); + last; + }; + + /^RECORD$/ && last; + /^FLUSH$/ && do { + my $dfh = $conn->{decoder_fh}; + print $dfh "flush\n"; + last; + }; + /^TEARDOWN$/ && do { + $resp->header('Connection', 'close'); + close $conn->{decoder_fh}; + last; + }; + /^SET_PARAMETER$/ && do { + my @lines = split /[\r\n]+/, $req->content; + my %content = map { /^(\S+): (.+)/; (lc $1, $2) } @lines; + my $cfh = $conn->{decoder_fh}; + if (exists $content{volume}) { + printf $cfh "vol: %f\n", $content{volume}; + } + last; + }; + /^GET_PARAMETER$/ && last; + /^DENIED$/ && last; + die("Unknown method: $_"); + } + + print $fh $resp->as_string("\r\n"); + $fh->flush; +} + +__DATA__ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt +wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U +wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf +/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/ +UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW +BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa +LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5 +NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm +lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz +aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu +a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM +oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z +oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+ +k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL +AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA +cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf +54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov +17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc +1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI +LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ +2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKaXTyY= +-----END RSA PRIVATE KEY-----