diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a7f54f7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,189 @@
+node-airtunes - a node.js implementation of AirTunes v2
+=======================================================
+
+I'm in a hurry
+--------------
+
+Installation
+
+
+ npm install airtunes
+
+
+The example folder contains several test scripts:
+
+* `cat sample.pcm | node play_stdin.js --host yourhost` will stream PCM data passed by stdin.
+* `play_ffmpeg.js` harnesses ffmpeg to stream audio files or URL.
+* `scan_airtunes.js` will list nearby AirTunes devices (OS X only).
+
+What is AirTunes ?
+------------------
+
+[AirTunes](http://en.wikipedia.org/wiki/AirTunes$) is a proprietary audio streaming protocol developed by Apple Inc. It allows wireless streaming of audio between devices. It is used today as the audio-streaming portion of AirPlay.
+
+AirTunes, AirPlay, RAOP ?
+-------------------------
+
+When AirTunes was introduced in 2004, its underlying protocol was called [RAOP](http://en.wikipedia.org/wiki/RAOP). It was based on RTSP/RTP and used a TCP transport. It was reverse-engineered in 2004 by Jon Lech Johansen, which opened the way to projects like [Shairport](https://github.com/albertz/shairport).
+
+RAOP didn't support synchronisation between separate streams so AirTunes underwent a major revision in 2011 to include advanced timing features. Its name was also changed to AirPlay. AirTunes v2 still uses RTSP/RTP but now uses a UDP transport.
+
+Most of the available open projects implement AirTunes v1. This is a problem because newer devices tend to drop support for this protocol.
+
+OK, now what is node-airtunes ?
+-------------------------------
+
+Node-airtunes is a node.js implementation of AirTunes v2. It supports synchronized audio output to any number of
+AirTunes receivers (like Apple's AirPort Express or AirFoil Speakers). It also allows synchronized local playback with CoreAudio (OS X only).
+
+Credits
+-------
+
+- [The Airtunes 2 Team](http://git.zx2c4.com/Airtunes2/about/)
+- Clément Vasseur for [Air Speaker](https://github.com/nto/AirSpeaker)
+- [Rogue Amoeba Software, LLC](http://www.rogueamoeba.com/) for AirFoil
+- Apple Inc and [Apple Lossless Audio Codec)[http://alac.macosforge.org/]
+
+Usage
+-----
+
+### Playback
+
+`airtunes` is a writable stream to which you can pipe 16bits, little-endian, stereo PCM data. Only this format is supported at the moment.
+
+```javascript
+var airtunes = require('airtunes');
+myPCMStream.pipe(airtunes);
+```
+
+The module has an internal circular buffer which makes it possible to stream from a network source. The stream will pause/resume to keep-up with the streaming. `airtunes` emits 'buffer' events when the status of its internal buffer changes:
+
+* `'buffering'`: Silence is being played while the buffer fills with data.
+* `'playing'`: Real sound is being streamed to devices.
+* `'end'`: The buffer has been closed by the input stream. Attempting to write more data will raise an exception.
+
+After an `end` event, you should close all devices with `airtunes.stopAll()` after 2s (most AirTunes devices have a 2s buffer). If you want to pipe several successive streams to airtunes, you must pass `{end: false}` to pipe.
+
+### Devices
+
+You can add devices at any time. The sound will be synchronized between all devices, regardless of when they were added. Node-airtunes does not do device discovery, but there's a small script in the examples folder that will do it for OS X users.
+
+```javascript
+var deviceKey = airtunes.add({
+ host: 'myairtunesdevice', // mandatory
+ port: 5000,
+ volume: 100,
+ password: 'mypassword'
+});
+```
+
+* `host` and `port` are the location of the AirTunes device as reported by Zeroconf. The default port is 5000.
+* `volume` is the initial volume, which must be between 0 and 100. The default volume is 50.
+* AirTunes makes it possible to protect devices with a `password`, which is of course optional. Bonjour indicates if the device demands a password.
+
+`airtunes` emits `'device'` events when the state of a device changes:
+
+```javascript
+airtunes.on('device', function(deviceKey, status, err) {
+ if(err)
+ console.log('device ' + deviceKey has an error: ' + err);
+ else
+ console.log('device ' + deviceKey + ' is ' + status);
+});
+```
+
+* `'playing'`: when a device is added, it emits this event when it is ready.
+* `'error'`: the device was removed from the pool because an error occured.
+
+Possible errors are:
+
+* 'connection_refused': The device refused the connection on the given port.
+* 'busy': Another application is already streaming to this device.
+* 'timeout': The device did not reply within 'config.rtsp_timeout'.
+* 'disconnected': The device closed the connection.
+* 'need_password': The device demands a password, but none was passed.
+* 'bad_password': The device refused the given password.
+* 'udp_ports': Could not bind UDP ports (these are required by AirPort v2).
+* 'rtsp_socket': Another RTSP error.
+
+You can stop devices with:
+
+```javascript
+airtunes.stop(deviceKey, function() {
+ // device was stopped
+});
+```
+
+You can stop everything with:
+
+```javascript
+airtunes.stopAll(function() {
+ // everything stopped
+});
+```
+
+### Volume
+
+You can change the volume with.
+
+```
+airtunes.setVolume(deviceKey, volume); // volume must be 0-100
+```
+
+Support
+-------
+
+Node-airtunes was tested on the following devices:
+
+* AirPort Express
+* AirFoil Speakers
+* Air Speaker
+* Freebox Server
+
+Ping me to add more devices to this list.
+
+How does it work ?
+------------------
+
+If you want detailed information about AirTunes v2, you should read the excellent documentation written by the [Airtunes 2 Team](http://git.zx2c4.com/Airtunes2/about/).
+
+Here's just enough information to follow the code.
+
+### RTSP Handshake
+
+RTSP is an HTTP-like protocol used to negociate parameters between the server (the output device) and us. AirTunes devices emit the 'playing' event when the handshake successfully completes.
+
+We send the following sequence:
+
+* _OPTIONS_: Apple added a proprietary 'Apple-Challenge' header so that iTunes can check if the receiving device is legit. We do send the header, but we don't check the challenge response.
+* _ANNOUNCE_: Among other things, we send an AES key and an IV (injection vector). The AES key is encrypted with a public RSA key shared by all AirTunes device. For simplicity's sake, we always use the same key/IV.
+* _SETUP_: We send UDP ports for control and timing. These ports are chosen before the handshake starts. The device replies with ports of its own.
+* _RECORD_: During record, we send the initial sequence and RTP time. These values allow devices to synchronize themselves.
+* _SET_PARAMETER_: Used to change the volume.
+* _TEARDOWN_: Used to terminate the connection.
+
+The RTSP socket stays open during the whole audio session. Since everything else is tranferred with UDP, closing the RTSP socket is the easiest way of terminating the session.
+
+### UDP Ports
+
+AirTunes v2 uses [RTP](http://en.wikipedia.org/wiki/Real-time_Transport_Protocol), which needs several UDP ports to transmit information. The ports are exchanged during the SETUP query.
+
+On the client (us):
+
+* Control: Used to send synchronization events and to receive information about resend packets.
+* Timing: Devices send queries to this port to synchronize their clocks with the client. The format follows [NTP](http://en.wikipedia.org/wiki/Network_Time_Protocol).
+
+On the device:
+
+* Control: every second, the client sends a synchronization message to all devices. This message contains the current time and says: "you should be playing the packet with this timestamp" right now.
+* Timing: the port where we send timing replies.
+* Audio: Where we send the audio stream.
+
+Each port has a preferred value (starting from UDP/6002). However, since ports can be used by other applications, we keep trying ports until we can bind both the control and the timing sockets. The sockets are bound only when there are active AirTunes devices.
+
+### Audio Streaming
+
+Now we get to the fun part. As stated earlier, the input audio must be 16bits, little-endian, stereo PCM. The stream is first split in chunks by the circular buffer, each chunk containing exactly 352 frames. It then compressed with [Apple Lossless](http://fr.wikipedia.org/wiki/Apple_Lossless) and encrypted with AES. The AES key is sent to devices during the _ANNOUNCE_ query. We use native code to compress and encrypt packets.
+
+The packets are then sent periodically. Since we have no congestion control, we must take great care to send packets at the right time. To achieve this, we trigger a timeout every 'config.stream_latency' ms. At each iteration, we compute the sequence number of the packet that we should be sending right now and we catch-up by sending in a burst all the packets that should have been sent since the last iteration. A higher latency reduces the CPU usage, but results in larger UDP bursts.
+
diff --git a/alac/ALACAudioTypes.h b/alac/ALACAudioTypes.h
new file mode 100644
index 0000000..f30e334
--- /dev/null
+++ b/alac/ALACAudioTypes.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: ALACAudioTypes.h
+*/
+
+#ifndef ALACAUDIOTYPES_H
+#define ALACAUDIOTYPES_H
+
+#if PRAGMA_ONCE
+#pragma once
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=mac68k
+#elif PRAGMA_STRUCT_PACKPUSH
+ #pragma pack(push, 2)
+#elif PRAGMA_STRUCT_PACK
+ #pragma pack(2)
+#endif
+
+#include
+
+#if defined(__ppc__)
+#define TARGET_RT_BIG_ENDIAN 1
+#elif defined(__ppc64__)
+#define TARGET_RT_BIG_ENDIAN 1
+#endif
+
+#define kChannelAtomSize 12
+
+enum
+{
+ kALAC_UnimplementedError = -4,
+ kALAC_FileNotFoundError = -43,
+ kALAC_ParamError = -50,
+ kALAC_MemFullError = -108
+};
+
+enum
+{
+ kALACFormatAppleLossless = 'alac',
+ kALACFormatLinearPCM = 'lpcm'
+};
+
+enum
+{
+ kALACMaxChannels = 8,
+ kALACMaxEscapeHeaderBytes = 8,
+ kALACMaxSearches = 16,
+ kALACMaxCoefs = 16,
+ kALACDefaultFramesPerPacket = 4096
+};
+
+typedef uint32_t ALACChannelLayoutTag;
+
+enum
+{
+ kALACFormatFlagIsFloat = (1 << 0), // 0x1
+ kALACFormatFlagIsBigEndian = (1 << 1), // 0x2
+ kALACFormatFlagIsSignedInteger = (1 << 2), // 0x4
+ kALACFormatFlagIsPacked = (1 << 3), // 0x8
+ kALACFormatFlagIsAlignedHigh = (1 << 4), // 0x10
+};
+
+enum
+{
+#if TARGET_RT_BIG_ENDIAN
+ kALACFormatFlagsNativeEndian = kALACFormatFlagIsBigEndian
+#else
+ kALACFormatFlagsNativeEndian = 0
+#endif
+};
+
+// this is required to be an IEEE 64bit float
+typedef double alac_float64_t;
+
+// These are the Channel Layout Tags used in the Channel Layout Info portion of the ALAC magic cookie
+enum
+{
+ kALACChannelLayoutTag_Mono = (100<<16) | 1, // C
+ kALACChannelLayoutTag_Stereo = (101<<16) | 2, // L R
+ kALACChannelLayoutTag_MPEG_3_0_B = (113<<16) | 3, // C L R
+ kALACChannelLayoutTag_MPEG_4_0_B = (116<<16) | 4, // C L R Cs
+ kALACChannelLayoutTag_MPEG_5_0_D = (120<<16) | 5, // C L R Ls Rs
+ kALACChannelLayoutTag_MPEG_5_1_D = (124<<16) | 6, // C L R Ls Rs LFE
+ kALACChannelLayoutTag_AAC_6_1 = (142<<16) | 7, // C L R Ls Rs Cs LFE
+ kALACChannelLayoutTag_MPEG_7_1_B = (127<<16) | 8 // C Lc Rc L R Ls Rs LFE (doc: IS-13818-7 MPEG2-AAC)
+};
+
+// ALAC currently only utilizes these channels layouts. There is a one for one correspondance between a
+// given number of channels and one of these layout tags
+static const ALACChannelLayoutTag ALACChannelLayoutTags[kALACMaxChannels] =
+{
+ kALACChannelLayoutTag_Mono, // C
+ kALACChannelLayoutTag_Stereo, // L R
+ kALACChannelLayoutTag_MPEG_3_0_B, // C L R
+ kALACChannelLayoutTag_MPEG_4_0_B, // C L R Cs
+ kALACChannelLayoutTag_MPEG_5_0_D, // C L R Ls Rs
+ kALACChannelLayoutTag_MPEG_5_1_D, // C L R Ls Rs LFE
+ kALACChannelLayoutTag_AAC_6_1, // C L R Ls Rs Cs LFE
+ kALACChannelLayoutTag_MPEG_7_1_B // C Lc Rc L R Ls Rs LFE (doc: IS-13818-7 MPEG2-AAC)
+};
+
+// AudioChannelLayout from CoreAudioTypes.h. We never need the AudioChannelDescription so we remove it
+struct ALACAudioChannelLayout
+{
+ ALACChannelLayoutTag mChannelLayoutTag;
+ uint32_t mChannelBitmap;
+ uint32_t mNumberChannelDescriptions;
+};
+typedef struct ALACAudioChannelLayout ALACAudioChannelLayout;
+
+struct AudioFormatDescription
+{
+ alac_float64_t mSampleRate;
+ uint32_t mFormatID;
+ uint32_t mFormatFlags;
+ uint32_t mBytesPerPacket;
+ uint32_t mFramesPerPacket;
+ uint32_t mBytesPerFrame;
+ uint32_t mChannelsPerFrame;
+ uint32_t mBitsPerChannel;
+ uint32_t mReserved;
+};
+typedef struct AudioFormatDescription AudioFormatDescription;
+
+/* Lossless Definitions */
+
+enum
+{
+ kALACCodecFormat = 'alac',
+ kALACVersion = 0,
+ kALACCompatibleVersion = kALACVersion,
+ kALACDefaultFrameSize = 4096
+};
+
+// note: this struct is wrapped in an 'alac' atom in the sample description extension area
+// note: in QT movies, it will be further wrapped in a 'wave' atom surrounded by 'frma' and 'term' atoms
+typedef struct ALACSpecificConfig
+{
+ uint32_t frameLength;
+ uint8_t compatibleVersion;
+ uint8_t bitDepth; // max 32
+ uint8_t pb; // 0 <= pb <= 255
+ uint8_t mb;
+ uint8_t kb;
+ uint8_t numChannels;
+ uint16_t maxRun;
+ uint32_t maxFrameBytes;
+ uint32_t avgBitRate;
+ uint32_t sampleRate;
+
+} ALACSpecificConfig;
+
+
+// The AudioChannelLayout atom type is not exposed yet so define it here
+enum
+{
+ AudioChannelLayoutAID = 'chan'
+};
+
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=reset
+#elif PRAGMA_STRUCT_PACKPUSH
+ #pragma pack(pop)
+#elif PRAGMA_STRUCT_PACK
+ #pragma pack()
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ALACAUDIOTYPES_H */
diff --git a/alac/ALACBitUtilities.c b/alac/ALACBitUtilities.c
new file mode 100644
index 0000000..9414889
--- /dev/null
+++ b/alac/ALACBitUtilities.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*=============================================================================
+ File: ALACBitUtilities.c
+
+ $NoKeywords: $
+=============================================================================*/
+
+#include
+#include "ALACBitUtilities.h"
+
+// BitBufferInit
+//
+void BitBufferInit( BitBuffer * bits, uint8_t * buffer, uint32_t byteSize )
+{
+ bits->cur = buffer;
+ bits->end = bits->cur + byteSize;
+ bits->bitIndex = 0;
+ bits->byteSize = byteSize;
+}
+
+// BitBufferRead
+//
+uint32_t BitBufferRead( BitBuffer * bits, uint8_t numBits )
+{
+ uint32_t returnBits;
+
+ //Assert( numBits <= 16 );
+
+ returnBits = ((uint32_t)bits->cur[0] << 16) | ((uint32_t)bits->cur[1] << 8) | ((uint32_t)bits->cur[2]);
+ returnBits = returnBits << bits->bitIndex;
+ returnBits &= 0x00FFFFFF;
+
+ bits->bitIndex += numBits;
+
+ returnBits = returnBits >> (24 - numBits);
+
+ bits->cur += (bits->bitIndex >> 3);
+ bits->bitIndex &= 7;
+
+ //Assert( bits->cur <= bits->end );
+
+ return returnBits;
+}
+
+// BitBufferReadSmall
+//
+// Reads up to 8 bits
+uint8_t BitBufferReadSmall( BitBuffer * bits, uint8_t numBits )
+{
+ uint16_t returnBits;
+
+ //Assert( numBits <= 8 );
+
+ returnBits = (bits->cur[0] << 8) | bits->cur[1];
+ returnBits = returnBits << bits->bitIndex;
+
+ bits->bitIndex += numBits;
+
+ returnBits = returnBits >> (16 - numBits);
+
+ bits->cur += (bits->bitIndex >> 3);
+ bits->bitIndex &= 7;
+
+ //Assert( bits->cur <= bits->end );
+
+ return (uint8_t)returnBits;
+}
+
+// BitBufferReadOne
+//
+// Reads one byte
+uint8_t BitBufferReadOne( BitBuffer * bits )
+{
+ uint8_t returnBits;
+
+ returnBits = (bits->cur[0] >> (7 - bits->bitIndex)) & 1;
+
+ bits->bitIndex++;
+
+ bits->cur += (bits->bitIndex >> 3);
+ bits->bitIndex &= 7;
+
+ //Assert( bits->cur <= bits->end );
+
+ return returnBits;
+}
+
+// BitBufferPeek
+//
+uint32_t BitBufferPeek( BitBuffer * bits, uint8_t numBits )
+{
+ return ((((((uint32_t) bits->cur[0] << 16) | ((uint32_t) bits->cur[1] << 8) |
+ ((uint32_t) bits->cur[2])) << bits->bitIndex) & 0x00FFFFFF) >> (24 - numBits));
+}
+
+// BitBufferPeekOne
+//
+uint32_t BitBufferPeekOne( BitBuffer * bits )
+{
+ return ((bits->cur[0] >> (7 - bits->bitIndex)) & 1);
+}
+
+// BitBufferUnpackBERSize
+//
+uint32_t BitBufferUnpackBERSize( BitBuffer * bits )
+{
+ uint32_t size;
+ uint8_t tmp;
+
+ for ( size = 0, tmp = 0x80u; tmp &= 0x80u; size = (size << 7u) | (tmp & 0x7fu) )
+ tmp = (uint8_t) BitBufferReadSmall( bits, 8 );
+
+ return size;
+}
+
+// BitBufferGetPosition
+//
+uint32_t BitBufferGetPosition( BitBuffer * bits )
+{
+ uint8_t * begin;
+
+ begin = bits->end - bits->byteSize;
+
+ return ((uint32_t)(bits->cur - begin) * 8) + bits->bitIndex;
+}
+
+// BitBufferByteAlign
+//
+void BitBufferByteAlign( BitBuffer * bits, int32_t addZeros )
+{
+ // align bit buffer to next byte boundary, writing zeros if requested
+ if ( bits->bitIndex == 0 )
+ return;
+
+ if ( addZeros )
+ BitBufferWrite( bits, 0, 8 - bits->bitIndex );
+ else
+ BitBufferAdvance( bits, 8 - bits->bitIndex );
+}
+
+// BitBufferAdvance
+//
+void BitBufferAdvance( BitBuffer * bits, uint32_t numBits )
+{
+ if ( numBits )
+ {
+ bits->bitIndex += numBits;
+ bits->cur += (bits->bitIndex >> 3);
+ bits->bitIndex &= 7;
+ }
+}
+
+// BitBufferRewind
+//
+void BitBufferRewind( BitBuffer * bits, uint32_t numBits )
+{
+ uint32_t numBytes;
+
+ if ( numBits == 0 )
+ return;
+
+ if ( bits->bitIndex >= numBits )
+ {
+ bits->bitIndex -= numBits;
+ return;
+ }
+
+ numBits -= bits->bitIndex;
+ bits->bitIndex = 0;
+
+ numBytes = numBits / 8;
+ numBits = numBits % 8;
+
+ bits->cur -= numBytes;
+
+ if ( numBits > 0 )
+ {
+ bits->bitIndex = 8 - numBits;
+ bits->cur--;
+ }
+
+ if ( bits->cur < (bits->end - bits->byteSize) )
+ {
+ //DebugCMsg("BitBufferRewind: Rewound too far.");
+
+ bits->cur = (bits->end - bits->byteSize);
+ bits->bitIndex = 0;
+ }
+}
+
+// BitBufferWrite
+//
+void BitBufferWrite( BitBuffer * bits, uint32_t bitValues, uint32_t numBits )
+{
+ uint32_t invBitIndex;
+
+ RequireAction( bits != nil, return; );
+ RequireActionSilent( numBits > 0, return; );
+
+ invBitIndex = 8 - bits->bitIndex;
+
+ while ( numBits > 0 )
+ {
+ uint32_t tmp;
+ uint8_t shift;
+ uint8_t mask;
+ uint32_t curNum;
+
+ curNum = MIN( invBitIndex, numBits );
+
+ tmp = bitValues >> (numBits - curNum);
+
+ shift = (uint8_t)(invBitIndex - curNum);
+ mask = 0xffu >> (8 - curNum); // must be done in two steps to avoid compiler sequencing ambiguity
+ mask <<= shift;
+
+ bits->cur[0] = (bits->cur[0] & ~mask) | (((uint8_t) tmp << shift) & mask);
+ numBits -= curNum;
+
+ // increment to next byte if need be
+ invBitIndex -= curNum;
+ if ( invBitIndex == 0 )
+ {
+ invBitIndex = 8;
+ bits->cur++;
+ }
+ }
+
+ bits->bitIndex = 8 - invBitIndex;
+}
+
+void BitBufferReset( BitBuffer * bits )
+//void BitBufferInit( BitBuffer * bits, uint8_t * buffer, uint32_t byteSize )
+{
+ bits->cur = bits->end - bits->byteSize;
+ bits->bitIndex = 0;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
diff --git a/alac/ALACBitUtilities.h b/alac/ALACBitUtilities.h
new file mode 100644
index 0000000..97e9ebe
--- /dev/null
+++ b/alac/ALACBitUtilities.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*=============================================================================
+ File: ALACBitUtilities.h
+
+ $NoKeywords: $
+=============================================================================*/
+
+#ifndef __ALACBITUTILITIES_H
+#define __ALACBITUTILITIES_H
+
+#include
+
+#ifndef MIN
+#define MIN(x, y) ( (x)<(y) ?(x) :(y) )
+#endif //MIN
+#ifndef MAX
+#define MAX(x, y) ( (x)>(y) ?(x): (y) )
+#endif //MAX
+
+#ifndef nil
+#define nil NULL
+#endif
+
+#define RequireAction(condition, action) if (!(condition)) { action }
+#define RequireActionSilent(condition, action) if (!(condition)) { action }
+#define RequireNoErr(condition, action) if ((condition)) { action }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum
+{
+ ALAC_noErr = 0
+};
+
+
+typedef enum
+{
+
+ ID_SCE = 0, /* Single Channel Element */
+ ID_CPE = 1, /* Channel Pair Element */
+ ID_CCE = 2, /* Coupling Channel Element */
+ ID_LFE = 3, /* LFE Channel Element */
+ ID_DSE = 4, /* not yet supported */
+ ID_PCE = 5,
+ ID_FIL = 6,
+ ID_END = 7
+} ELEMENT_TYPE;
+
+// types
+typedef struct BitBuffer
+{
+ uint8_t * cur;
+ uint8_t * end;
+ uint32_t bitIndex;
+ uint32_t byteSize;
+
+} BitBuffer;
+
+/*
+ BitBuffer routines
+ - these routines take a fixed size buffer and read/write to it
+ - bounds checking must be done by the client
+*/
+void BitBufferInit( BitBuffer * bits, uint8_t * buffer, uint32_t byteSize );
+uint32_t BitBufferRead( BitBuffer * bits, uint8_t numBits ); // note: cannot read more than 16 bits at a time
+uint8_t BitBufferReadSmall( BitBuffer * bits, uint8_t numBits );
+uint8_t BitBufferReadOne( BitBuffer * bits );
+uint32_t BitBufferPeek( BitBuffer * bits, uint8_t numBits ); // note: cannot read more than 16 bits at a time
+uint32_t BitBufferPeekOne( BitBuffer * bits );
+uint32_t BitBufferUnpackBERSize( BitBuffer * bits );
+uint32_t BitBufferGetPosition( BitBuffer * bits );
+void BitBufferByteAlign( BitBuffer * bits, int32_t addZeros );
+void BitBufferAdvance( BitBuffer * bits, uint32_t numBits );
+void BitBufferRewind( BitBuffer * bits, uint32_t numBits );
+void BitBufferWrite( BitBuffer * bits, uint32_t value, uint32_t numBits );
+void BitBufferReset( BitBuffer * bits);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __BITUTILITIES_H */
diff --git a/alac/ALACDecoder.cpp b/alac/ALACDecoder.cpp
new file mode 100644
index 0000000..ce3340d
--- /dev/null
+++ b/alac/ALACDecoder.cpp
@@ -0,0 +1,730 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: ALACDecoder.cpp
+*/
+
+#include
+#include
+
+#include "ALACDecoder.h"
+
+#include "dplib.h"
+#include "aglib.h"
+#include "matrixlib.h"
+
+#include "ALACBitUtilities.h"
+#include "EndianPortable.h"
+
+// constants/data
+const uint32_t kMaxBitDepth = 32; // max allowed bit depth is 32
+
+
+// prototypes
+static void Zero16( int16_t * buffer, uint32_t numItems, uint32_t stride );
+static void Zero24( uint8_t * buffer, uint32_t numItems, uint32_t stride );
+static void Zero32( int32_t * buffer, uint32_t numItems, uint32_t stride );
+
+/*
+ Constructor
+*/
+ALACDecoder::ALACDecoder() :
+ mMixBufferU( nil ),
+ mMixBufferV( nil ),
+ mPredictor( nil ),
+ mShiftBuffer( nil )
+{
+ memset( &mConfig, 0, sizeof(mConfig) );
+}
+
+/*
+ Destructor
+*/
+ALACDecoder::~ALACDecoder()
+{
+ // delete the matrix mixing buffers
+ if ( mMixBufferU )
+ {
+ free(mMixBufferU);
+ mMixBufferU = NULL;
+ }
+ if ( mMixBufferV )
+ {
+ free(mMixBufferV);
+ mMixBufferV = NULL;
+ }
+
+ // delete the dynamic predictor's "corrector" buffer
+ // - note: mShiftBuffer shares memory with this buffer
+ if ( mPredictor )
+ {
+ free(mPredictor);
+ mPredictor = NULL;
+ }
+}
+
+/*
+ Init()
+ - initialize the decoder with the given configuration
+*/
+int32_t ALACDecoder::Init( void * inMagicCookie, uint32_t inMagicCookieSize )
+{
+ int32_t status = ALAC_noErr;
+ ALACSpecificConfig theConfig;
+ uint8_t * theActualCookie = (uint8_t *)inMagicCookie;
+ uint32_t theCookieBytesRemaining = inMagicCookieSize;
+
+ // For historical reasons the decoder needs to be resilient to magic cookies vended by older encoders.
+ // As specified in the ALACMagicCookieDescription.txt document, there may be additional data encapsulating
+ // the ALACSpecificConfig. This would consist of format ('frma') and 'alac' atoms which precede the
+ // ALACSpecificConfig.
+ // See ALACMagicCookieDescription.txt for additional documentation concerning the 'magic cookie'
+
+ // skip format ('frma') atom if present
+ if (theActualCookie[4] == 'f' && theActualCookie[5] == 'r' && theActualCookie[6] == 'm' && theActualCookie[7] == 'a')
+ {
+ theActualCookie += 12;
+ theCookieBytesRemaining -= 12;
+ }
+
+ // skip 'alac' atom header if present
+ if (theActualCookie[4] == 'a' && theActualCookie[5] == 'l' && theActualCookie[6] == 'a' && theActualCookie[7] == 'c')
+ {
+ theActualCookie += 12;
+ theCookieBytesRemaining -= 12;
+ }
+
+ // read the ALACSpecificConfig
+ if (theCookieBytesRemaining >= sizeof(ALACSpecificConfig))
+ {
+ theConfig.frameLength = Swap32BtoN(((ALACSpecificConfig *)theActualCookie)->frameLength);
+ theConfig.compatibleVersion = ((ALACSpecificConfig *)theActualCookie)->compatibleVersion;
+ theConfig.bitDepth = ((ALACSpecificConfig *)theActualCookie)->bitDepth;
+ theConfig.pb = ((ALACSpecificConfig *)theActualCookie)->pb;
+ theConfig.mb = ((ALACSpecificConfig *)theActualCookie)->mb;
+ theConfig.kb = ((ALACSpecificConfig *)theActualCookie)->kb;
+ theConfig.numChannels = ((ALACSpecificConfig *)theActualCookie)->numChannels;
+ theConfig.maxRun = Swap16BtoN(((ALACSpecificConfig *)theActualCookie)->maxRun);
+ theConfig.maxFrameBytes = Swap32BtoN(((ALACSpecificConfig *)theActualCookie)->maxFrameBytes);
+ theConfig.avgBitRate = Swap32BtoN(((ALACSpecificConfig *)theActualCookie)->avgBitRate);
+ theConfig.sampleRate = Swap32BtoN(((ALACSpecificConfig *)theActualCookie)->sampleRate);
+
+ mConfig = theConfig;
+
+ RequireAction( mConfig.compatibleVersion <= kALACVersion, return kALAC_ParamError; );
+
+ // allocate mix buffers
+ mMixBufferU = (int32_t *) calloc( mConfig.frameLength * sizeof(int32_t), 1 );
+ mMixBufferV = (int32_t *) calloc( mConfig.frameLength * sizeof(int32_t), 1 );
+
+ // allocate dynamic predictor buffer
+ mPredictor = (int32_t *) calloc( mConfig.frameLength * sizeof(int32_t), 1 );
+
+ // "shift off" buffer shares memory with predictor buffer
+ mShiftBuffer = (uint16_t *) mPredictor;
+
+ RequireAction( (mMixBufferU != nil) && (mMixBufferV != nil) && (mPredictor != nil),
+ status = kALAC_MemFullError; goto Exit; );
+ }
+ else
+ {
+ status = kALAC_ParamError;
+ }
+
+ // skip to Channel Layout Info
+ // theActualCookie += sizeof(ALACSpecificConfig);
+
+ // Currently, the Channel Layout Info portion of the magic cookie (as defined in the
+ // ALACMagicCookieDescription.txt document) is unused by the decoder.
+
+Exit:
+ return status;
+}
+
+/*
+ Decode()
+ - the decoded samples are interleaved into the output buffer in the order they arrive in
+ the bitstream
+*/
+int32_t ALACDecoder::Decode( BitBuffer * bits, uint8_t * sampleBuffer, uint32_t numSamples, uint32_t numChannels, uint32_t * outNumSamples )
+{
+ BitBuffer shiftBits;
+ uint32_t bits1, bits2;
+ uint8_t tag;
+ uint8_t elementInstanceTag;
+ AGParamRec agParams;
+ uint32_t channelIndex;
+ int16_t coefsU[32]; // max possible size is 32 although NUMCOEPAIRS is the current limit
+ int16_t coefsV[32];
+ uint8_t numU, numV;
+ uint8_t mixBits;
+ int8_t mixRes;
+ uint16_t unusedHeader;
+ uint8_t escapeFlag;
+ uint32_t chanBits;
+ uint8_t bytesShifted;
+ uint32_t shift;
+ uint8_t modeU, modeV;
+ uint32_t denShiftU, denShiftV;
+ uint16_t pbFactorU, pbFactorV;
+ uint16_t pb;
+ int16_t * samples;
+ int16_t * out16;
+ uint8_t * out20;
+ uint8_t * out24;
+ int32_t * out32;
+ uint8_t headerByte;
+ uint8_t partialFrame;
+ uint32_t extraBits;
+ int32_t val;
+ uint32_t i, j;
+ int32_t status;
+
+ RequireAction( (bits != nil) && (sampleBuffer != nil) && (outNumSamples != nil), return kALAC_ParamError; );
+ RequireAction( numChannels > 0, return kALAC_ParamError; );
+
+ mActiveElements = 0;
+ channelIndex = 0;
+
+ samples = (int16_t *) sampleBuffer;
+
+ status = ALAC_noErr;
+ *outNumSamples = numSamples;
+
+ while ( status == ALAC_noErr )
+ {
+ // bail if we ran off the end of the buffer
+ RequireAction( bits->cur < bits->end, status = kALAC_ParamError; goto Exit; );
+
+ // copy global decode params for this element
+ pb = mConfig.pb;
+
+ // read element tag
+ tag = BitBufferReadSmall( bits, 3 );
+ switch ( tag )
+ {
+ case ID_SCE:
+ case ID_LFE:
+ {
+ // mono/LFE channel
+ elementInstanceTag = BitBufferReadSmall( bits, 4 );
+ mActiveElements |= (1u << elementInstanceTag);
+
+ // read the 12 unused header bits
+ unusedHeader = (uint16_t) BitBufferRead( bits, 12 );
+ RequireAction( unusedHeader == 0, status = kALAC_ParamError; goto Exit; );
+
+ // read the 1-bit "partial frame" flag, 2-bit "shift-off" flag & 1-bit "escape" flag
+ headerByte = (uint8_t) BitBufferRead( bits, 4 );
+
+ partialFrame = headerByte >> 3;
+
+ bytesShifted = (headerByte >> 1) & 0x3u;
+ RequireAction( bytesShifted != 3, status = kALAC_ParamError; goto Exit; );
+
+ shift = bytesShifted * 8;
+
+ escapeFlag = headerByte & 0x1;
+
+ chanBits = mConfig.bitDepth - (bytesShifted * 8);
+
+ // check for partial frame to override requested numSamples
+ if ( partialFrame != 0 )
+ {
+ numSamples = BitBufferRead( bits, 16 ) << 16;
+ numSamples |= BitBufferRead( bits, 16 );
+ }
+
+ if ( escapeFlag == 0 )
+ {
+ // compressed frame, read rest of parameters
+ mixBits = (uint8_t) BitBufferRead( bits, 8 );
+ mixRes = (int8_t) BitBufferRead( bits, 8 );
+ //Assert( (mixBits == 0) && (mixRes == 0) ); // no mixing for mono
+
+ headerByte = (uint8_t) BitBufferRead( bits, 8 );
+ modeU = headerByte >> 4;
+ denShiftU = headerByte & 0xfu;
+
+ headerByte = (uint8_t) BitBufferRead( bits, 8 );
+ pbFactorU = headerByte >> 5;
+ numU = headerByte & 0x1fu;
+
+ for ( i = 0; i < numU; i++ )
+ coefsU[i] = (int16_t) BitBufferRead( bits, 16 );
+
+ // if shift active, skip the the shift buffer but remember where it starts
+ if ( bytesShifted != 0 )
+ {
+ shiftBits = *bits;
+ BitBufferAdvance( bits, (bytesShifted * 8) * numSamples );
+ }
+
+ // decompress
+ set_ag_params( &agParams, mConfig.mb, (pb * pbFactorU) / 4, mConfig.kb, numSamples, numSamples, mConfig.maxRun );
+ status = dyn_decomp( &agParams, bits, mPredictor, numSamples, chanBits, &bits1 );
+ RequireNoErr( status, goto Exit; );
+
+ if ( modeU == 0 )
+ {
+ unpc_block( mPredictor, mMixBufferU, numSamples, &coefsU[0], numU, chanBits, denShiftU );
+ }
+ else
+ {
+ // the special "numActive == 31" mode can be done in-place
+ unpc_block( mPredictor, mPredictor, numSamples, nil, 31, chanBits, 0 );
+ unpc_block( mPredictor, mMixBufferU, numSamples, &coefsU[0], numU, chanBits, denShiftU );
+ }
+ }
+ else
+ {
+ //Assert( bytesShifted == 0 );
+
+ // uncompressed frame, copy data into the mix buffer to use common output code
+ shift = 32 - chanBits;
+ if ( chanBits <= 16 )
+ {
+ for ( i = 0; i < numSamples; i++ )
+ {
+ val = (int32_t) BitBufferRead( bits, (uint8_t) chanBits );
+ val = (val << shift) >> shift;
+ mMixBufferU[i] = val;
+ }
+ }
+ else
+ {
+ // BitBufferRead() can't read more than 16 bits at a time so break up the reads
+ extraBits = chanBits - 16;
+ for ( i = 0; i < numSamples; i++ )
+ {
+ val = (int32_t) BitBufferRead( bits, 16 );
+ val = (val << 16) >> shift;
+ mMixBufferU[i] = val | BitBufferRead( bits, (uint8_t) extraBits );
+ }
+ }
+
+ mixBits = mixRes = 0;
+ bits1 = chanBits * numSamples;
+ bytesShifted = 0;
+ }
+
+ // now read the shifted values into the shift buffer
+ if ( bytesShifted != 0 )
+ {
+ shift = bytesShifted * 8;
+ //Assert( shift <= 16 );
+
+ for ( i = 0; i < numSamples; i++ )
+ mShiftBuffer[i] = (uint16_t) BitBufferRead( &shiftBits, (uint8_t) shift );
+ }
+
+ // convert 32-bit integers into output buffer
+ switch ( mConfig.bitDepth )
+ {
+ case 16:
+ out16 = &((int16_t *)sampleBuffer)[channelIndex];
+ for ( i = 0, j = 0; i < numSamples; i++, j += numChannels )
+ out16[j] = (int16_t) mMixBufferU[i];
+ break;
+ case 20:
+ out20 = (uint8_t *)sampleBuffer + (channelIndex * 3);
+ copyPredictorTo20( mMixBufferU, out20, numChannels, numSamples );
+ break;
+ case 24:
+ out24 = (uint8_t *)sampleBuffer + (channelIndex * 3);
+ if ( bytesShifted != 0 )
+ copyPredictorTo24Shift( mMixBufferU, mShiftBuffer, out24, numChannels, numSamples, bytesShifted );
+ else
+ copyPredictorTo24( mMixBufferU, out24, numChannels, numSamples );
+ break;
+ case 32:
+ out32 = &((int32_t *)sampleBuffer)[channelIndex];
+ if ( bytesShifted != 0 )
+ copyPredictorTo32Shift( mMixBufferU, mShiftBuffer, out32, numChannels, numSamples, bytesShifted );
+ else
+ copyPredictorTo32( mMixBufferU, out32, numChannels, numSamples);
+ break;
+ }
+
+ channelIndex += 1;
+ *outNumSamples = numSamples;
+ break;
+ }
+
+ case ID_CPE:
+ {
+ // if decoding this pair would take us over the max channels limit, bail
+ if ( (channelIndex + 2) > numChannels )
+ goto NoMoreChannels;
+
+ // stereo channel pair
+ elementInstanceTag = BitBufferReadSmall( bits, 4 );
+ mActiveElements |= (1u << elementInstanceTag);
+
+ // read the 12 unused header bits
+ unusedHeader = (uint16_t) BitBufferRead( bits, 12 );
+ RequireAction( unusedHeader == 0, status = kALAC_ParamError; goto Exit; );
+
+ // read the 1-bit "partial frame" flag, 2-bit "shift-off" flag & 1-bit "escape" flag
+ headerByte = (uint8_t) BitBufferRead( bits, 4 );
+
+ partialFrame = headerByte >> 3;
+
+ bytesShifted = (headerByte >> 1) & 0x3u;
+ RequireAction( bytesShifted != 3, status = kALAC_ParamError; goto Exit; );
+
+ shift = bytesShifted * 8;
+
+ escapeFlag = headerByte & 0x1;
+
+ chanBits = mConfig.bitDepth - (bytesShifted * 8) + 1;
+
+ // check for partial frame length to override requested numSamples
+ if ( partialFrame != 0 )
+ {
+ numSamples = BitBufferRead( bits, 16 ) << 16;
+ numSamples |= BitBufferRead( bits, 16 );
+ }
+
+ if ( escapeFlag == 0 )
+ {
+ // compressed frame, read rest of parameters
+ mixBits = (uint8_t) BitBufferRead( bits, 8 );
+ mixRes = (int8_t) BitBufferRead( bits, 8 );
+
+ headerByte = (uint8_t) BitBufferRead( bits, 8 );
+ modeU = headerByte >> 4;
+ denShiftU = headerByte & 0xfu;
+
+ headerByte = (uint8_t) BitBufferRead( bits, 8 );
+ pbFactorU = headerByte >> 5;
+ numU = headerByte & 0x1fu;
+ for ( i = 0; i < numU; i++ )
+ coefsU[i] = (int16_t) BitBufferRead( bits, 16 );
+
+ headerByte = (uint8_t) BitBufferRead( bits, 8 );
+ modeV = headerByte >> 4;
+ denShiftV = headerByte & 0xfu;
+
+ headerByte = (uint8_t) BitBufferRead( bits, 8 );
+ pbFactorV = headerByte >> 5;
+ numV = headerByte & 0x1fu;
+ for ( i = 0; i < numV; i++ )
+ coefsV[i] = (int16_t) BitBufferRead( bits, 16 );
+
+ // if shift active, skip the interleaved shifted values but remember where they start
+ if ( bytesShifted != 0 )
+ {
+ shiftBits = *bits;
+ BitBufferAdvance( bits, (bytesShifted * 8) * 2 * numSamples );
+ }
+
+ // decompress and run predictor for "left" channel
+ set_ag_params( &agParams, mConfig.mb, (pb * pbFactorU) / 4, mConfig.kb, numSamples, numSamples, mConfig.maxRun );
+ status = dyn_decomp( &agParams, bits, mPredictor, numSamples, chanBits, &bits1 );
+ RequireNoErr( status, goto Exit; );
+
+ if ( modeU == 0 )
+ {
+ unpc_block( mPredictor, mMixBufferU, numSamples, &coefsU[0], numU, chanBits, denShiftU );
+ }
+ else
+ {
+ // the special "numActive == 31" mode can be done in-place
+ unpc_block( mPredictor, mPredictor, numSamples, nil, 31, chanBits, 0 );
+ unpc_block( mPredictor, mMixBufferU, numSamples, &coefsU[0], numU, chanBits, denShiftU );
+ }
+
+ // decompress and run predictor for "right" channel
+ set_ag_params( &agParams, mConfig.mb, (pb * pbFactorV) / 4, mConfig.kb, numSamples, numSamples, mConfig.maxRun );
+ status = dyn_decomp( &agParams, bits, mPredictor, numSamples, chanBits, &bits2 );
+ RequireNoErr( status, goto Exit; );
+
+ if ( modeV == 0 )
+ {
+ unpc_block( mPredictor, mMixBufferV, numSamples, &coefsV[0], numV, chanBits, denShiftV );
+ }
+ else
+ {
+ // the special "numActive == 31" mode can be done in-place
+ unpc_block( mPredictor, mPredictor, numSamples, nil, 31, chanBits, 0 );
+ unpc_block( mPredictor, mMixBufferV, numSamples, &coefsV[0], numV, chanBits, denShiftV );
+ }
+ }
+ else
+ {
+ //Assert( bytesShifted == 0 );
+
+ // uncompressed frame, copy data into the mix buffers to use common output code
+ chanBits = mConfig.bitDepth;
+ shift = 32 - chanBits;
+ if ( chanBits <= 16 )
+ {
+ for ( i = 0; i < numSamples; i++ )
+ {
+ val = (int32_t) BitBufferRead( bits, (uint8_t) chanBits );
+ val = (val << shift) >> shift;
+ mMixBufferU[i] = val;
+
+ val = (int32_t) BitBufferRead( bits, (uint8_t) chanBits );
+ val = (val << shift) >> shift;
+ mMixBufferV[i] = val;
+ }
+ }
+ else
+ {
+ // BitBufferRead() can't read more than 16 bits at a time so break up the reads
+ extraBits = chanBits - 16;
+ for ( i = 0; i < numSamples; i++ )
+ {
+ val = (int32_t) BitBufferRead( bits, 16 );
+ val = (val << 16) >> shift;
+ mMixBufferU[i] = val | BitBufferRead( bits, (uint8_t)extraBits );
+
+ val = (int32_t) BitBufferRead( bits, 16 );
+ val = (val << 16) >> shift;
+ mMixBufferV[i] = val | BitBufferRead( bits, (uint8_t)extraBits );
+ }
+ }
+
+ bits1 = chanBits * numSamples;
+ bits2 = chanBits * numSamples;
+ mixBits = mixRes = 0;
+ bytesShifted = 0;
+ }
+
+ // now read the shifted values into the shift buffer
+ if ( bytesShifted != 0 )
+ {
+ shift = bytesShifted * 8;
+ //Assert( shift <= 16 );
+
+ for ( i = 0; i < (numSamples * 2); i += 2 )
+ {
+ mShiftBuffer[i + 0] = (uint16_t) BitBufferRead( &shiftBits, (uint8_t) shift );
+ mShiftBuffer[i + 1] = (uint16_t) BitBufferRead( &shiftBits, (uint8_t) shift );
+ }
+ }
+
+ // un-mix the data and convert to output format
+ // - note that mixRes = 0 means just interleave so we use that path for uncompressed frames
+ switch ( mConfig.bitDepth )
+ {
+ case 16:
+ out16 = &((int16_t *)sampleBuffer)[channelIndex];
+ unmix16( mMixBufferU, mMixBufferV, out16, numChannels, numSamples, mixBits, mixRes );
+ break;
+ case 20:
+ out20 = (uint8_t *)sampleBuffer + (channelIndex * 3);
+ unmix20( mMixBufferU, mMixBufferV, out20, numChannels, numSamples, mixBits, mixRes );
+ break;
+ case 24:
+ out24 = (uint8_t *)sampleBuffer + (channelIndex * 3);
+ unmix24( mMixBufferU, mMixBufferV, out24, numChannels, numSamples,
+ mixBits, mixRes, mShiftBuffer, bytesShifted );
+ break;
+ case 32:
+ out32 = &((int32_t *)sampleBuffer)[channelIndex];
+ unmix32( mMixBufferU, mMixBufferV, out32, numChannels, numSamples,
+ mixBits, mixRes, mShiftBuffer, bytesShifted );
+ break;
+ }
+
+ channelIndex += 2;
+ *outNumSamples = numSamples;
+ break;
+ }
+
+ case ID_CCE:
+ case ID_PCE:
+ {
+ // unsupported element, bail
+ //AssertNoErr( tag );
+ status = kALAC_ParamError;
+ break;
+ }
+
+ case ID_DSE:
+ {
+ // data stream element -- parse but ignore
+ status = this->DataStreamElement( bits );
+ break;
+ }
+
+ case ID_FIL:
+ {
+ // fill element -- parse but ignore
+ status = this->FillElement( bits );
+ break;
+ }
+
+ case ID_END:
+ {
+ // frame end, all done so byte align the frame and check for overruns
+ BitBufferByteAlign( bits, false );
+ //Assert( bits->cur == bits->end );
+ goto Exit;
+ }
+ }
+
+#if ! DEBUG
+ // if we've decoded all of our channels, bail (but not in debug b/c we want to know if we're seeing bad bits)
+ // - this also protects us if the config does not match the bitstream or crap data bits follow the audio bits
+ if ( channelIndex >= numChannels )
+ break;
+#endif
+ }
+
+NoMoreChannels:
+
+ // if we get here and haven't decoded all of the requested channels, fill the remaining channels with zeros
+ for ( ; channelIndex < numChannels; channelIndex++ )
+ {
+ switch ( mConfig.bitDepth )
+ {
+ case 16:
+ {
+ int16_t * fill16 = &((int16_t *)sampleBuffer)[channelIndex];
+ Zero16( fill16, numSamples, numChannels );
+ break;
+ }
+ case 24:
+ {
+ uint8_t * fill24 = (uint8_t *)sampleBuffer + (channelIndex * 3);
+ Zero24( fill24, numSamples, numChannels );
+ break;
+ }
+ case 32:
+ {
+ int32_t * fill32 = &((int32_t *)sampleBuffer)[channelIndex];
+ Zero32( fill32, numSamples, numChannels );
+ break;
+ }
+ }
+ }
+
+Exit:
+ return status;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+/*
+ FillElement()
+ - they're just filler so we don't need 'em
+*/
+int32_t ALACDecoder::FillElement( BitBuffer * bits )
+{
+ int16_t count;
+
+ // 4-bit count or (4-bit + 8-bit count) if 4-bit count == 15
+ // - plus this weird -1 thing I still don't fully understand
+ count = BitBufferReadSmall( bits, 4 );
+ if ( count == 15 )
+ count += (int16_t) BitBufferReadSmall( bits, 8 ) - 1;
+
+ BitBufferAdvance( bits, count * 8 );
+
+ RequireAction( bits->cur <= bits->end, return kALAC_ParamError; );
+
+ return ALAC_noErr;
+}
+
+/*
+ DataStreamElement()
+ - we don't care about data stream elements so just skip them
+*/
+int32_t ALACDecoder::DataStreamElement( BitBuffer * bits )
+{
+ uint8_t element_instance_tag;
+ int32_t data_byte_align_flag;
+ uint16_t count;
+
+ // the tag associates this data stream element with a given audio element
+ element_instance_tag = BitBufferReadSmall( bits, 4 );
+
+ data_byte_align_flag = BitBufferReadOne( bits );
+
+ // 8-bit count or (8-bit + 8-bit count) if 8-bit count == 255
+ count = BitBufferReadSmall( bits, 8 );
+ if ( count == 255 )
+ count += BitBufferReadSmall( bits, 8 );
+
+ // the align flag means the bitstream should be byte-aligned before reading the following data bytes
+ if ( data_byte_align_flag )
+ BitBufferByteAlign( bits, false );
+
+ // skip the data bytes
+ BitBufferAdvance( bits, count * 8 );
+
+ RequireAction( bits->cur <= bits->end, return kALAC_ParamError; );
+
+ return ALAC_noErr;
+}
+
+/*
+ ZeroN()
+ - helper routines to clear out output channel buffers when decoding fewer channels than requested
+*/
+static void Zero16( int16_t * buffer, uint32_t numItems, uint32_t stride )
+{
+ if ( stride == 1 )
+ {
+ memset( buffer, 0, numItems * sizeof(int16_t) );
+ }
+ else
+ {
+ for ( uint32_t index = 0; index < (numItems * stride); index += stride )
+ buffer[index] = 0;
+ }
+}
+
+static void Zero24( uint8_t * buffer, uint32_t numItems, uint32_t stride )
+{
+ if ( stride == 1 )
+ {
+ memset( buffer, 0, numItems * 3 );
+ }
+ else
+ {
+ for ( uint32_t index = 0; index < (numItems * stride * 3); index += (stride * 3) )
+ {
+ buffer[index + 0] = 0;
+ buffer[index + 1] = 0;
+ buffer[index + 2] = 0;
+ }
+ }
+}
+
+static void Zero32( int32_t * buffer, uint32_t numItems, uint32_t stride )
+{
+ if ( stride == 1 )
+ {
+ memset( buffer, 0, numItems * sizeof(int32_t) );
+ }
+ else
+ {
+ for ( uint32_t index = 0; index < (numItems * stride); index += stride )
+ buffer[index] = 0;
+ }
+}
diff --git a/alac/ALACDecoder.h b/alac/ALACDecoder.h
new file mode 100644
index 0000000..7802eab
--- /dev/null
+++ b/alac/ALACDecoder.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: ALACDecoder.h
+*/
+
+#ifndef _ALACDECODER_H
+#define _ALACDECODER_H
+
+#if PRAGMA_ONCE
+#pragma once
+#endif
+
+#include
+
+#include "ALACAudioTypes.h"
+
+struct BitBuffer;
+
+class ALACDecoder
+{
+ public:
+ ALACDecoder();
+ ~ALACDecoder();
+
+ int32_t Init( void * inMagicCookie, uint32_t inMagicCookieSize );
+ int32_t Decode( struct BitBuffer * bits, uint8_t * sampleBuffer, uint32_t numSamples, uint32_t numChannels, uint32_t * outNumSamples );
+
+ public:
+ // decoding parameters (public for use in the analyzer)
+ ALACSpecificConfig mConfig;
+
+ protected:
+ int32_t FillElement( struct BitBuffer * bits );
+ int32_t DataStreamElement( struct BitBuffer * bits );
+
+ uint16_t mActiveElements;
+
+ // decoding buffers
+ int32_t * mMixBufferU;
+ int32_t * mMixBufferV;
+ int32_t * mPredictor;
+ uint16_t * mShiftBuffer; // note: this points to mPredictor's memory but different
+ // variable for clarity and type difference
+};
+
+#endif /* _ALACDECODER_H */
diff --git a/alac/ALACEncoder.cpp b/alac/ALACEncoder.cpp
new file mode 100644
index 0000000..1b71c29
--- /dev/null
+++ b/alac/ALACEncoder.cpp
@@ -0,0 +1,1425 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: ALACEncoder.cpp
+*/
+
+// build stuff
+#define VERBOSE_DEBUG 0
+
+// headers
+#include
+#include
+#include
+
+#include "ALACEncoder.h"
+
+#include "aglib.h"
+#include "dplib.h"
+#include "matrixlib.h"
+
+#include "ALACBitUtilities.h"
+#include "ALACAudioTypes.h"
+#include "EndianPortable.h"
+
+// Note: in C you can't typecast to a 2-dimensional array pointer but that's what we need when
+// picking which coefs to use so we declare this typedef b/c we *can* typecast to this type
+typedef int16_t (*SearchCoefs)[kALACMaxCoefs];
+
+// defines/constants
+const uint32_t kALACEncoderMagic = 'dpge';
+const uint32_t kMaxSampleSize = 32; // max allowed bit width is 32
+const uint32_t kDefaultMixBits = 2;
+const uint32_t kDefaultMixRes = 0;
+const uint32_t kMaxRes = 4;
+const uint32_t kDefaultNumUV = 8;
+const uint32_t kMinUV = 4;
+const uint32_t kMaxUV = 8;
+
+// static functions
+#if VERBOSE_DEBUG
+static void AddFiller( BitBuffer * bits, int32_t numBytes );
+#endif
+
+
+/*
+ Map Format: 3-bit field per channel which is the same as the "element tag" that should be placed
+ at the beginning of the frame for that channel. Indicates whether SCE, CPE, or LFE.
+ Each particular field is accessed via the current channel index. Note that the channel
+ index increments by two for channel pairs.
+
+ For example:
+
+ C L R 3-channel input = (ID_CPE << 3) | (ID_SCE)
+ index 0 value = (map & (0x7ul << (0 * 3))) >> (0 * 3)
+ index 1 value = (map & (0x7ul << (1 * 3))) >> (1 * 3)
+
+ C L R Ls Rs LFE 5.1-channel input = (ID_LFE << 15) | (ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE)
+ index 0 value = (map & (0x7ul << (0 * 3))) >> (0 * 3)
+ index 1 value = (map & (0x7ul << (1 * 3))) >> (1 * 3)
+ index 3 value = (map & (0x7ul << (3 * 3))) >> (3 * 3)
+ index 5 value = (map & (0x7ul << (5 * 3))) >> (5 * 3)
+ index 7 value = (map & (0x7ul << (7 * 3))) >> (7 * 3)
+*/
+static const uint32_t sChannelMaps[kALACMaxChannels] =
+{
+ ID_SCE,
+ ID_CPE,
+ (ID_CPE << 3) | (ID_SCE),
+ (ID_SCE << 9) | (ID_CPE << 3) | (ID_SCE),
+ (ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE),
+ (ID_SCE << 15) | (ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE),
+ (ID_SCE << 18) | (ID_SCE << 15) | (ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE),
+ (ID_SCE << 21) | (ID_CPE << 15) | (ID_CPE << 9) | (ID_CPE << 3) | (ID_SCE)
+};
+
+static const uint32_t sSupportediPodSampleRates[] =
+{
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+/*
+ Constructor
+*/
+ALACEncoder::ALACEncoder() :
+ mBitDepth( 0 ),
+ mFastMode( 0 ),
+ mMixBufferU( nil ),
+ mMixBufferV( nil ),
+ mPredictorU( nil ),
+ mPredictorV( nil ),
+ mShiftBufferUV( nil ),
+ mWorkBuffer( nil ),
+
+
+ mTotalBytesGenerated( 0 ),
+ mAvgBitRate( 0 ),
+ mMaxFrameBytes( 0 )
+{
+ // overrides
+ mFrameSize = kALACDefaultFrameSize;
+}
+
+/*
+ Destructor
+*/
+ALACEncoder::~ALACEncoder()
+{
+ // delete the matrix mixing buffers
+ if ( mMixBufferU )
+ {
+ free(mMixBufferU);
+ mMixBufferU = NULL;
+ }
+ if ( mMixBufferV )
+ {
+ free(mMixBufferV);
+ mMixBufferV = NULL;
+ }
+
+ // delete the dynamic predictor's "corrector" buffers
+ if ( mPredictorU )
+ {
+ free(mPredictorU);
+ mPredictorU = NULL;
+ }
+ if ( mPredictorV )
+ {
+ free(mPredictorV);
+ mPredictorV = NULL;
+ }
+
+ // delete the unused byte shift buffer
+ if ( mShiftBufferUV )
+ {
+ free(mShiftBufferUV);
+ mShiftBufferUV = NULL;
+ }
+
+ // delete the work buffer
+ if ( mWorkBuffer )
+ {
+ free(mWorkBuffer);
+ mWorkBuffer = NULL;
+ }
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+/*
+ HEADER SPECIFICATION
+
+ For every segment we adopt the following header:
+
+ 1 byte reserved (always 0)
+ 1 byte flags (see below)
+ [4 byte frame length] (optional, see below)
+ ---Next, the per-segment ALAC parameters---
+ 1 byte mixBits (middle-side parameter)
+ 1 byte mixRes (middle-side parameter, interpreted as signed char)
+
+ 1 byte shiftU (4 bits modeU, 4 bits denShiftU)
+ 1 byte filterU (3 bits pbFactorU, 5 bits numU)
+ (numU) shorts (signed DP coefficients for V channel)
+ ---Next, 2nd-channel ALAC parameters in case of stereo mode---
+ 1 byte shiftV (4 bits modeV, 4 bits denShiftV)
+ 1 byte filterV (3 bits pbFactorV, 5 bits numV)
+ (numV) shorts (signed DP coefficients for V channel)
+ ---After this come the shift-off bytes for (>= 24)-bit data (n-byte shift) if indicated---
+ ---Then comes the AG-compressor bitstream---
+
+
+ FLAGS
+ -----
+
+ The presence of certain flag bits changes the header format such that the parameters might
+ not even be sent. The currently defined flags format is:
+
+ 0000psse
+
+ where 0 = reserved, must be 0
+ p = 1-bit field "partial frame" flag indicating 32-bit frame length follows this byte
+ ss = 2-bit field indicating "number of shift-off bytes ignored by compression"
+ e = 1-bit field indicating "escape"
+
+ The "partial frame" flag means that the following segment is not equal to the frame length specified
+ in the out-of-band decoder configuration. This allows the decoder to deal with end-of-file partial
+ segments without incurring the 32-bit overhead for each segment.
+
+ The "shift-off" field indicates the number of bytes at the bottom of the word that were passed through
+ uncompressed. The reason for this is that the entropy inherent in the LS bytes of >= 24-bit words
+ quite often means that the frame would have to be "escaped" b/c the compressed size would be >= the
+ uncompressed size. However, by shifting the input values down and running the remaining bits through
+ the normal compression algorithm, a net win can be achieved. If this field is non-zero, it means that
+ the shifted-off bytes follow after the parameter section of the header and before the compressed
+ bitstream. Note that doing this also allows us to use matrixing on 32-bit inputs after one or more
+ bytes are shifted off the bottom which helps the eventual compression ratio. For stereo channels,
+ the shifted off bytes are interleaved.
+
+ The "escape" flag means that this segment was not compressed b/c the compressed size would be
+ >= uncompressed size. In that case, the audio data was passed through uncompressed after the header.
+ The other header parameter bytes will not be sent.
+
+
+ PARAMETERS
+ ----------
+
+ If the segment is not a partial or escape segment, the total header size (in bytes) is given exactly by:
+
+ 4 + (2 + 2 * numU) (mono mode)
+ 4 + (2 + 2 * numV) + (2 + 2 * numV) (stereo mode)
+
+ where the ALAC filter-lengths numU, numV are bounded by a
+ constant (in the current source, numU, numV <= NUMCOEPAIRS), and
+ this forces an absolute upper bound on header size.
+
+ Each segment-decode process loads up these bytes from the front of the
+ local stream, in the above order, then follows with the entropy-encoded
+ bits for the given segment.
+
+ To generalize middle-side, there are various mixing modes including middle-side, each lossless,
+ as embodied in the mix() and unmix() functions. These functions exploit a generalized middle-side
+ transformation:
+
+ u := [(rL + (m-r)R)/m];
+ v := L - R;
+
+ where [ ] denotes integer floor. The (lossless) inverse is
+
+ L = u + v - [rV/m];
+ R = L - v;
+
+ In the segment header, m and r are encoded in mixBits and mixRes.
+ Classical "middle-side" is obtained with m = 2, r = 1, but now
+ we have more generalized mixes.
+
+ NOTES
+ -----
+ The relevance of the ALAC coefficients is explained in detail
+ in patent documents.
+*/
+
+/*
+ EncodeStereo()
+ - encode a channel pair
+*/
+int32_t ALACEncoder::EncodeStereo( BitBuffer * bitstream, void * inputBuffer, uint32_t stride, uint32_t channelIndex, uint32_t numSamples )
+{
+ BitBuffer workBits;
+ BitBuffer startBits = *bitstream; // squirrel away copy of current state in case we need to go back and do an escape packet
+ AGParamRec agParams;
+ uint32_t bits1, bits2;
+ uint32_t dilate;
+ int32_t mixBits, mixRes, maxRes;
+ uint32_t minBits, minBits1, minBits2;
+ uint32_t numU, numV;
+ uint32_t mode;
+ uint32_t pbFactor;
+ uint32_t chanBits;
+ uint32_t denShift;
+ uint8_t bytesShifted;
+ SearchCoefs coefsU;
+ SearchCoefs coefsV;
+ uint32_t index;
+ uint8_t partialFrame;
+ uint32_t escapeBits;
+ bool doEscape;
+ int32_t status = ALAC_noErr;
+
+ // make sure we handle this bit-depth before we get going
+ RequireAction( (mBitDepth == 16) || (mBitDepth == 20) || (mBitDepth == 24) || (mBitDepth == 32), return kALAC_ParamError; );
+
+ // reload coefs pointers for this channel pair
+ // - note that, while you might think they should be re-initialized per block, retaining state across blocks
+ // actually results in better overall compression
+ // - strangely, re-using the same coefs for the different passes of the "mixRes" search loop instead of using
+ // different coefs for the different passes of "mixRes" results in even better compression
+ coefsU = (SearchCoefs) mCoefsU[channelIndex];
+ coefsV = (SearchCoefs) mCoefsV[channelIndex];
+
+ // matrix encoding adds an extra bit but 32-bit inputs cannot be matrixed b/c 33 is too many
+ // so enable 16-bit "shift off" and encode in 17-bit mode
+ // - in addition, 24-bit mode really improves with one byte shifted off
+ if ( mBitDepth == 32 )
+ bytesShifted = 2;
+ else if ( mBitDepth >= 24 )
+ bytesShifted = 1;
+ else
+ bytesShifted = 0;
+
+ chanBits = mBitDepth - (bytesShifted * 8) + 1;
+
+ // flag whether or not this is a partial frame
+ partialFrame = (numSamples == mFrameSize) ? 0 : 1;
+
+ // brute-force encode optimization loop
+ // - run over variations of the encoding params to find the best choice
+ mixBits = kDefaultMixBits;
+ maxRes = kMaxRes;
+ numU = numV = kDefaultNumUV;
+ denShift = DENSHIFT_DEFAULT;
+ mode = 0;
+ pbFactor = 4;
+ dilate = 8;
+
+ minBits = minBits1 = minBits2 = 1ul << 31;
+
+ int32_t bestRes = mLastMixRes[channelIndex];
+
+ for ( mixRes = 0; mixRes <= maxRes; mixRes++ )
+ {
+ // mix the stereo inputs
+ switch ( mBitDepth )
+ {
+ case 16:
+ mix16( (int16_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples/dilate, mixBits, mixRes );
+ break;
+ case 20:
+ mix20( (uint8_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples/dilate, mixBits, mixRes );
+ break;
+ case 24:
+ // includes extraction of shifted-off bytes
+ mix24( (uint8_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples/dilate,
+ mixBits, mixRes, mShiftBufferUV, bytesShifted );
+ break;
+ case 32:
+ // includes extraction of shifted-off bytes
+ mix32( (int32_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples/dilate,
+ mixBits, mixRes, mShiftBufferUV, bytesShifted );
+ break;
+ }
+
+ BitBufferInit( &workBits, mWorkBuffer, mMaxOutputBytes );
+
+ // run the dynamic predictors
+ pc_block( mMixBufferU, mPredictorU, numSamples/dilate, coefsU[numU - 1], numU, chanBits, DENSHIFT_DEFAULT );
+ pc_block( mMixBufferV, mPredictorV, numSamples/dilate, coefsV[numV - 1], numV, chanBits, DENSHIFT_DEFAULT );
+
+ // run the lossless compressor on each channel
+ set_ag_params( &agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples/dilate, numSamples/dilate, MAX_RUN_DEFAULT );
+ status = dyn_comp( &agParams, mPredictorU, &workBits, numSamples/dilate, chanBits, &bits1 );
+ RequireNoErr( status, goto Exit; );
+
+ set_ag_params( &agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples/dilate, numSamples/dilate, MAX_RUN_DEFAULT );
+ status = dyn_comp( &agParams, mPredictorV, &workBits, numSamples/dilate, chanBits, &bits2 );
+ RequireNoErr( status, goto Exit; );
+
+ // look for best match
+ if ( (bits1 + bits2) < minBits1 )
+ {
+ minBits1 = bits1 + bits2;
+ bestRes = mixRes;
+ }
+ }
+
+ mLastMixRes[channelIndex] = (int16_t)bestRes;
+
+ // mix the stereo inputs with the current best mixRes
+ mixRes = mLastMixRes[channelIndex];
+ switch ( mBitDepth )
+ {
+ case 16:
+ mix16( (int16_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples, mixBits, mixRes );
+ break;
+ case 20:
+ mix20( (uint8_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples, mixBits, mixRes );
+ break;
+ case 24:
+ // also extracts the shifted off bytes into the shift buffers
+ mix24( (uint8_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples,
+ mixBits, mixRes, mShiftBufferUV, bytesShifted );
+ break;
+ case 32:
+ // also extracts the shifted off bytes into the shift buffers
+ mix32( (int32_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples,
+ mixBits, mixRes, mShiftBufferUV, bytesShifted );
+ break;
+ }
+
+ // now it's time for the predictor coefficient search loop
+ numU = numV = kMinUV;
+ minBits1 = minBits2 = 1ul << 31;
+
+ for ( uint32_t numUV = kMinUV; numUV <= kMaxUV; numUV += 4 )
+ {
+ BitBufferInit( &workBits, mWorkBuffer, mMaxOutputBytes );
+
+ dilate = 32;
+
+ // run the predictor over the same data multiple times to help it converge
+ for ( uint32_t converge = 0; converge < 8; converge++ )
+ {
+ pc_block( mMixBufferU, mPredictorU, numSamples/dilate, coefsU[numUV-1], numUV, chanBits, DENSHIFT_DEFAULT );
+ pc_block( mMixBufferV, mPredictorV, numSamples/dilate, coefsV[numUV-1], numUV, chanBits, DENSHIFT_DEFAULT );
+ }
+
+ dilate = 8;
+
+ set_ag_params( &agParams, MB0, (pbFactor * PB0)/4, KB0, numSamples/dilate, numSamples/dilate, MAX_RUN_DEFAULT );
+ status = dyn_comp( &agParams, mPredictorU, &workBits, numSamples/dilate, chanBits, &bits1 );
+
+ if ( (bits1 * dilate + 16 * numUV) < minBits1 )
+ {
+ minBits1 = bits1 * dilate + 16 * numUV;
+ numU = numUV;
+ }
+
+ set_ag_params( &agParams, MB0, (pbFactor * PB0)/4, KB0, numSamples/dilate, numSamples/dilate, MAX_RUN_DEFAULT );
+ status = dyn_comp( &agParams, mPredictorV, &workBits, numSamples/dilate, chanBits, &bits2 );
+
+ if ( (bits2 * dilate + 16 * numUV) < minBits2 )
+ {
+ minBits2 = bits2 * dilate + 16 * numUV;
+ numV = numUV;
+ }
+ }
+
+ // test for escape hatch if best calculated compressed size turns out to be more than the input size
+ minBits = minBits1 + minBits2 + (8 /* mixRes/maxRes/etc. */ * 8) + ((partialFrame == true) ? 32 : 0);
+ if ( bytesShifted != 0 )
+ minBits += (numSamples * (bytesShifted * 8) * 2);
+
+ escapeBits = (numSamples * mBitDepth * 2) + ((partialFrame == true) ? 32 : 0) + (2 * 8); /* 2 common header bytes */
+
+ doEscape = (minBits >= escapeBits) ? true : false;
+
+ if ( doEscape == false )
+ {
+ // write bitstream header and coefs
+ BitBufferWrite( bitstream, 0, 12 );
+ BitBufferWrite( bitstream, (partialFrame << 3) | (bytesShifted << 1), 4 );
+ if ( partialFrame )
+ BitBufferWrite( bitstream, numSamples, 32 );
+ BitBufferWrite( bitstream, mixBits, 8 );
+ BitBufferWrite( bitstream, mixRes, 8 );
+
+ //Assert( (mode < 16) && (DENSHIFT_DEFAULT < 16) );
+ //Assert( (pbFactor < 8) && (numU < 32) );
+ //Assert( (pbFactor < 8) && (numV < 32) );
+
+ BitBufferWrite( bitstream, (mode << 4) | DENSHIFT_DEFAULT, 8 );
+ BitBufferWrite( bitstream, (pbFactor << 5) | numU, 8 );
+ for ( index = 0; index < numU; index++ )
+ BitBufferWrite( bitstream, coefsU[numU - 1][index], 16 );
+
+ BitBufferWrite( bitstream, (mode << 4) | DENSHIFT_DEFAULT, 8 );
+ BitBufferWrite( bitstream, (pbFactor << 5) | numV, 8 );
+ for ( index = 0; index < numV; index++ )
+ BitBufferWrite( bitstream, coefsV[numV - 1][index], 16 );
+
+ // if shift active, write the interleaved shift buffers
+ if ( bytesShifted != 0 )
+ {
+ uint32_t bitShift = bytesShifted * 8;
+
+ //Assert( bitShift <= 16 );
+
+ for ( index = 0; index < (numSamples * 2); index += 2 )
+ {
+ uint32_t shiftedVal;
+
+ shiftedVal = ((uint32_t)mShiftBufferUV[index + 0] << bitShift) | (uint32_t)mShiftBufferUV[index + 1];
+ BitBufferWrite( bitstream, shiftedVal, bitShift * 2 );
+ }
+ }
+
+ // run the dynamic predictor and lossless compression for the "left" channel
+ // - note: to avoid allocating more buffers, we're mixing and matching between the available buffers instead
+ // of only using "U" buffers for the U-channel and "V" buffers for the V-channel
+ if ( mode == 0 )
+ {
+ pc_block( mMixBufferU, mPredictorU, numSamples, coefsU[numU - 1], numU, chanBits, DENSHIFT_DEFAULT );
+ }
+ else
+ {
+ pc_block( mMixBufferU, mPredictorV, numSamples, coefsU[numU - 1], numU, chanBits, DENSHIFT_DEFAULT );
+ pc_block( mPredictorV, mPredictorU, numSamples, nil, 31, chanBits, 0 );
+ }
+
+ set_ag_params( &agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples, numSamples, MAX_RUN_DEFAULT );
+ status = dyn_comp( &agParams, mPredictorU, bitstream, numSamples, chanBits, &bits1 );
+ RequireNoErr( status, goto Exit; );
+
+ // run the dynamic predictor and lossless compression for the "right" channel
+ if ( mode == 0 )
+ {
+ pc_block( mMixBufferV, mPredictorV, numSamples, coefsV[numV - 1], numV, chanBits, DENSHIFT_DEFAULT );
+ }
+ else
+ {
+ pc_block( mMixBufferV, mPredictorU, numSamples, coefsV[numV - 1], numV, chanBits, DENSHIFT_DEFAULT );
+ pc_block( mPredictorU, mPredictorV, numSamples, nil, 31, chanBits, 0 );
+ }
+
+ set_ag_params( &agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples, numSamples, MAX_RUN_DEFAULT );
+ status = dyn_comp( &agParams, mPredictorV, bitstream, numSamples, chanBits, &bits2 );
+ RequireNoErr( status, goto Exit; );
+
+ /* if we happened to create a compressed packet that was actually bigger than an escape packet would be,
+ chuck it and do an escape packet
+ */
+ minBits = BitBufferGetPosition( bitstream ) - BitBufferGetPosition( &startBits );
+ if ( minBits >= escapeBits )
+ {
+ *bitstream = startBits; // reset bitstream state
+ doEscape = true;
+ printf( "compressed frame too big: %u vs. %u \n", minBits, escapeBits );
+ }
+ }
+
+ if ( doEscape == true )
+ {
+ /* escape */
+ status = this->EncodeStereoEscape( bitstream, inputBuffer, stride, numSamples );
+
+#if VERBOSE_DEBUG
+ DebugMsg( "escape!: %lu vs %lu", minBits, escapeBits );
+#endif
+ }
+
+Exit:
+ return status;
+}
+
+/*
+ EncodeStereoFast()
+ - encode a channel pair without the search loop for maximum possible speed
+*/
+int32_t ALACEncoder::EncodeStereoFast( BitBuffer * bitstream, void * inputBuffer, uint32_t stride, uint32_t channelIndex, uint32_t numSamples )
+{
+ BitBuffer startBits = *bitstream; // squirrel away current bit position in case we decide to use escape hatch
+ AGParamRec agParams;
+ uint32_t bits1, bits2;
+ int32_t mixBits, mixRes;
+ uint32_t minBits, minBits1, minBits2;
+ uint32_t numU, numV;
+ uint32_t mode;
+ uint32_t pbFactor;
+ uint32_t chanBits;
+ uint32_t denShift;
+ uint8_t bytesShifted;
+ SearchCoefs coefsU;
+ SearchCoefs coefsV;
+ uint32_t index;
+ uint8_t partialFrame;
+ uint32_t escapeBits;
+ bool doEscape;
+ int32_t status;
+
+ // make sure we handle this bit-depth before we get going
+ RequireAction( (mBitDepth == 16) || (mBitDepth == 20) || (mBitDepth == 24) || (mBitDepth == 32), return kALAC_ParamError; );
+
+ // reload coefs pointers for this channel pair
+ // - note that, while you might think they should be re-initialized per block, retaining state across blocks
+ // actually results in better overall compression
+ // - strangely, re-using the same coefs for the different passes of the "mixRes" search loop instead of using
+ // different coefs for the different passes of "mixRes" results in even better compression
+ coefsU = (SearchCoefs) mCoefsU[channelIndex];
+ coefsV = (SearchCoefs) mCoefsV[channelIndex];
+
+ // matrix encoding adds an extra bit but 32-bit inputs cannot be matrixed b/c 33 is too many
+ // so enable 16-bit "shift off" and encode in 17-bit mode
+ // - in addition, 24-bit mode really improves with one byte shifted off
+ if ( mBitDepth == 32 )
+ bytesShifted = 2;
+ else if ( mBitDepth >= 24 )
+ bytesShifted = 1;
+ else
+ bytesShifted = 0;
+
+ chanBits = mBitDepth - (bytesShifted * 8) + 1;
+
+ // flag whether or not this is a partial frame
+ partialFrame = (numSamples == mFrameSize) ? 0 : 1;
+
+ // set up default encoding parameters for "fast" mode
+ mixBits = kDefaultMixBits;
+ mixRes = kDefaultMixRes;
+ numU = numV = kDefaultNumUV;
+ denShift = DENSHIFT_DEFAULT;
+ mode = 0;
+ pbFactor = 4;
+
+ minBits = minBits1 = minBits2 = 1ul << 31;
+
+ // mix the stereo inputs with default mixBits/mixRes
+ switch ( mBitDepth )
+ {
+ case 16:
+ mix16( (int16_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples, mixBits, mixRes );
+ break;
+ case 20:
+ mix20( (uint8_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples, mixBits, mixRes );
+ break;
+ case 24:
+ // also extracts the shifted off bytes into the shift buffers
+ mix24( (uint8_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples,
+ mixBits, mixRes, mShiftBufferUV, bytesShifted );
+ break;
+ case 32:
+ // also extracts the shifted off bytes into the shift buffers
+ mix32( (int32_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples,
+ mixBits, mixRes, mShiftBufferUV, bytesShifted );
+ break;
+ }
+
+ /* speculatively write the bitstream assuming the compressed version will be smaller */
+
+ // write bitstream header and coefs
+ BitBufferWrite( bitstream, 0, 12 );
+ BitBufferWrite( bitstream, (partialFrame << 3) | (bytesShifted << 1), 4 );
+ if ( partialFrame )
+ BitBufferWrite( bitstream, numSamples, 32 );
+ BitBufferWrite( bitstream, mixBits, 8 );
+ BitBufferWrite( bitstream, mixRes, 8 );
+
+ //Assert( (mode < 16) && (DENSHIFT_DEFAULT < 16) );
+ //Assert( (pbFactor < 8) && (numU < 32) );
+ //Assert( (pbFactor < 8) && (numV < 32) );
+
+ BitBufferWrite( bitstream, (mode << 4) | DENSHIFT_DEFAULT, 8 );
+ BitBufferWrite( bitstream, (pbFactor << 5) | numU, 8 );
+ for ( index = 0; index < numU; index++ )
+ BitBufferWrite( bitstream, coefsU[numU - 1][index], 16 );
+
+ BitBufferWrite( bitstream, (mode << 4) | DENSHIFT_DEFAULT, 8 );
+ BitBufferWrite( bitstream, (pbFactor << 5) | numV, 8 );
+ for ( index = 0; index < numV; index++ )
+ BitBufferWrite( bitstream, coefsV[numV - 1][index], 16 );
+
+ // if shift active, write the interleaved shift buffers
+ if ( bytesShifted != 0 )
+ {
+ uint32_t bitShift = bytesShifted * 8;
+
+ //Assert( bitShift <= 16 );
+
+ for ( index = 0; index < (numSamples * 2); index += 2 )
+ {
+ uint32_t shiftedVal;
+
+ shiftedVal = ((uint32_t)mShiftBufferUV[index + 0] << bitShift) | (uint32_t)mShiftBufferUV[index + 1];
+ BitBufferWrite( bitstream, shiftedVal, bitShift * 2 );
+ }
+ }
+
+ // run the dynamic predictor and lossless compression for the "left" channel
+ // - note: we always use mode 0 in the "fast" path so we don't need the code for mode != 0
+ pc_block( mMixBufferU, mPredictorU, numSamples, coefsU[numU - 1], numU, chanBits, DENSHIFT_DEFAULT );
+
+ set_ag_params( &agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples, numSamples, MAX_RUN_DEFAULT );
+ status = dyn_comp( &agParams, mPredictorU, bitstream, numSamples, chanBits, &bits1 );
+ RequireNoErr( status, goto Exit; );
+
+ // run the dynamic predictor and lossless compression for the "right" channel
+ pc_block( mMixBufferV, mPredictorV, numSamples, coefsV[numV - 1], numV, chanBits, DENSHIFT_DEFAULT );
+
+ set_ag_params( &agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples, numSamples, MAX_RUN_DEFAULT );
+ status = dyn_comp( &agParams, mPredictorV, bitstream, numSamples, chanBits, &bits2 );
+ RequireNoErr( status, goto Exit; );
+
+ // do bit requirement calculations
+ minBits1 = bits1 + (numU * sizeof(int16_t) * 8);
+ minBits2 = bits2 + (numV * sizeof(int16_t) * 8);
+
+ // test for escape hatch if best calculated compressed size turns out to be more than the input size
+ minBits = minBits1 + minBits2 + (8 /* mixRes/maxRes/etc. */ * 8) + ((partialFrame == true) ? 32 : 0);
+ if ( bytesShifted != 0 )
+ minBits += (numSamples * (bytesShifted * 8) * 2);
+
+ escapeBits = (numSamples * mBitDepth * 2) + ((partialFrame == true) ? 32 : 0) + (2 * 8); /* 2 common header bytes */
+
+ doEscape = (minBits >= escapeBits) ? true : false;
+
+ if ( doEscape == false )
+ {
+ /* if we happened to create a compressed packet that was actually bigger than an escape packet would be,
+ chuck it and do an escape packet
+ */
+ minBits = BitBufferGetPosition( bitstream ) - BitBufferGetPosition( &startBits );
+ if ( minBits >= escapeBits )
+ {
+ doEscape = true;
+ printf( "compressed frame too big: %u vs. %u\n", minBits, escapeBits );
+ }
+
+ }
+
+ if ( doEscape == true )
+ {
+ /* escape */
+
+ // reset bitstream position since we speculatively wrote the compressed version
+ *bitstream = startBits;
+
+ // write escape frame
+ status = this->EncodeStereoEscape( bitstream, inputBuffer, stride, numSamples );
+
+#if VERBOSE_DEBUG
+ DebugMsg( "escape!: %u vs %u", minBits, (numSamples * mBitDepth * 2) );
+#endif
+ }
+
+Exit:
+ return status;
+}
+
+/*
+ EncodeStereoEscape()
+ - encode stereo escape frame
+*/
+int32_t ALACEncoder::EncodeStereoEscape( BitBuffer * bitstream, void * inputBuffer, uint32_t stride, uint32_t numSamples )
+{
+ int16_t * input16;
+ int32_t * input32;
+ uint8_t partialFrame;
+ uint32_t index;
+
+ // flag whether or not this is a partial frame
+ partialFrame = (numSamples == mFrameSize) ? 0 : 1;
+
+ // write bitstream header
+ BitBufferWrite( bitstream, 0, 12 );
+ BitBufferWrite( bitstream, (partialFrame << 3) | 1, 4 ); // LSB = 1 means "frame not compressed"
+ if ( partialFrame )
+ BitBufferWrite( bitstream, numSamples, 32 );
+
+ // just copy the input data to the output buffer
+ switch ( mBitDepth )
+ {
+ case 16:
+ input16 = (int16_t *) inputBuffer;
+
+ for ( index = 0; index < (numSamples * stride); index += stride )
+ {
+ BitBufferWrite( bitstream, input16[index + 0], 16 );
+ BitBufferWrite( bitstream, input16[index + 1], 16 );
+ }
+ break;
+ case 20:
+ // mix20() with mixres param = 0 means de-interleave so use it to simplify things
+ mix20( (uint8_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples, 0, 0 );
+ for ( index = 0; index < numSamples; index++ )
+ {
+ BitBufferWrite( bitstream, mMixBufferU[index], 20 );
+ BitBufferWrite( bitstream, mMixBufferV[index], 20 );
+ }
+ break;
+ case 24:
+ // mix24() with mixres param = 0 means de-interleave so use it to simplify things
+ mix24( (uint8_t *) inputBuffer, stride, mMixBufferU, mMixBufferV, numSamples, 0, 0, mShiftBufferUV, 0 );
+ for ( index = 0; index < numSamples; index++ )
+ {
+ BitBufferWrite( bitstream, mMixBufferU[index], 24 );
+ BitBufferWrite( bitstream, mMixBufferV[index], 24 );
+ }
+ break;
+ case 32:
+ input32 = (int32_t *) inputBuffer;
+
+ for ( index = 0; index < (numSamples * stride); index += stride )
+ {
+ BitBufferWrite( bitstream, input32[index + 0], 32 );
+ BitBufferWrite( bitstream, input32[index + 1], 32 );
+ }
+ break;
+ }
+
+ return ALAC_noErr;
+}
+
+/*
+ EncodeMono()
+ - encode a mono input buffer
+*/
+int32_t ALACEncoder::EncodeMono( BitBuffer * bitstream, void * inputBuffer, uint32_t stride, uint32_t channelIndex, uint32_t numSamples )
+{
+ BitBuffer startBits = *bitstream; // squirrel away copy of current state in case we need to go back and do an escape packet
+ AGParamRec agParams;
+ uint32_t bits1;
+ uint32_t numU;
+ SearchCoefs coefsU;
+ uint32_t dilate;
+ uint32_t minBits, bestU;
+ uint32_t minU, maxU;
+ uint32_t index, index2;
+ uint8_t bytesShifted;
+ uint32_t shift;
+ uint32_t mask;
+ uint32_t chanBits;
+ uint8_t pbFactor;
+ uint8_t partialFrame;
+ int16_t * input16;
+ int32_t * input32;
+ uint32_t escapeBits;
+ bool doEscape;
+ int32_t status;
+
+ // make sure we handle this bit-depth before we get going
+ RequireAction( (mBitDepth == 16) || (mBitDepth == 20) || (mBitDepth == 24) || (mBitDepth == 32), return kALAC_ParamError; );
+
+ status = ALAC_noErr;
+
+ // reload coefs array from previous frame
+ coefsU = (SearchCoefs) mCoefsU[channelIndex];
+
+ // pick bit depth for actual encoding
+ // - we lop off the lower byte(s) for 24-/32-bit encodings
+ if ( mBitDepth == 32 )
+ bytesShifted = 2;
+ else if ( mBitDepth >= 24 )
+ bytesShifted = 1;
+ else
+ bytesShifted = 0;
+
+ shift = bytesShifted * 8;
+ mask = (1ul << shift) - 1;
+ chanBits = mBitDepth - (bytesShifted * 8);
+
+ // flag whether or not this is a partial frame
+ partialFrame = (numSamples == mFrameSize) ? 0 : 1;
+
+ // convert N-bit data to 32-bit for predictor
+ switch ( mBitDepth )
+ {
+ case 16:
+ {
+ // convert 16-bit data to 32-bit for predictor
+ input16 = (int16_t *) inputBuffer;
+ for ( index = 0, index2 = 0; index < numSamples; index++, index2 += stride )
+ mMixBufferU[index] = (int32_t) input16[index2];
+ break;
+ }
+ case 20:
+ // convert 20-bit data to 32-bit for predictor
+ copy20ToPredictor( (uint8_t *) inputBuffer, stride, mMixBufferU, numSamples );
+ break;
+ case 24:
+ // convert 24-bit data to 32-bit for the predictor and extract the shifted off byte(s)
+ copy24ToPredictor( (uint8_t *) inputBuffer, stride, mMixBufferU, numSamples );
+ for ( index = 0; index < numSamples; index++ )
+ {
+ mShiftBufferUV[index] = (uint16_t)(mMixBufferU[index] & mask);
+ mMixBufferU[index] >>= shift;
+ }
+ break;
+ case 32:
+ {
+ // just copy the 32-bit input data for the predictor and extract the shifted off byte(s)
+ input32 = (int32_t *) inputBuffer;
+
+ for ( index = 0, index2 = 0; index < numSamples; index++, index2 += stride )
+ {
+ int32_t val = input32[index2];
+
+ mShiftBufferUV[index] = (uint16_t)(val & mask);
+ mMixBufferU[index] = val >> shift;
+ }
+ break;
+ }
+ }
+
+ // brute-force encode optimization loop (implied "encode depth" of 0 if comparing to cmd line tool)
+ // - run over variations of the encoding params to find the best choice
+ minU = 4;
+ maxU = 8;
+ minBits = 1ul << 31;
+ pbFactor = 4;
+
+ minBits = 1ul << 31;
+ bestU = minU;
+
+ for ( numU = minU; numU <= maxU; numU += 4 )
+ {
+ BitBuffer workBits;
+ uint32_t numBits;
+
+ BitBufferInit( &workBits, mWorkBuffer, mMaxOutputBytes );
+
+ dilate = 32;
+ for ( uint32_t converge = 0; converge < 7; converge++ )
+ pc_block( mMixBufferU, mPredictorU, numSamples/dilate, coefsU[numU-1], numU, chanBits, DENSHIFT_DEFAULT );
+
+ dilate = 8;
+ pc_block( mMixBufferU, mPredictorU, numSamples/dilate, coefsU[numU-1], numU, chanBits, DENSHIFT_DEFAULT );
+
+ set_ag_params( &agParams, MB0, (pbFactor * PB0) / 4, KB0, numSamples/dilate, numSamples/dilate, MAX_RUN_DEFAULT );
+ status = dyn_comp( &agParams, mPredictorU, &workBits, numSamples/dilate, chanBits, &bits1 );
+ RequireNoErr( status, goto Exit; );
+
+ numBits = (dilate * bits1) + (16 * numU);
+ if ( numBits < minBits )
+ {
+ bestU = numU;
+ minBits = numBits;
+ }
+ }
+
+ // test for escape hatch if best calculated compressed size turns out to be more than the input size
+ // - first, add bits for the header bytes mixRes/maxRes/shiftU/filterU
+ minBits += (4 /* mixRes/maxRes/etc. */ * 8) + ((partialFrame == true) ? 32 : 0);
+ if ( bytesShifted != 0 )
+ minBits += (numSamples * (bytesShifted * 8));
+
+ escapeBits = (numSamples * mBitDepth) + ((partialFrame == true) ? 32 : 0) + (2 * 8); /* 2 common header bytes */
+
+ doEscape = (minBits >= escapeBits) ? true : false;
+
+ if ( doEscape == false )
+ {
+ // write bitstream header
+ BitBufferWrite( bitstream, 0, 12 );
+ BitBufferWrite( bitstream, (partialFrame << 3) | (bytesShifted << 1), 4 );
+ if ( partialFrame )
+ BitBufferWrite( bitstream, numSamples, 32 );
+ BitBufferWrite( bitstream, 0, 16 ); // mixBits = mixRes = 0
+
+ // write the params and predictor coefs
+ numU = bestU;
+ BitBufferWrite( bitstream, (0 << 4) | DENSHIFT_DEFAULT, 8 ); // modeU = 0
+ BitBufferWrite( bitstream, (pbFactor << 5) | numU, 8 );
+ for ( index = 0; index < numU; index++ )
+ BitBufferWrite( bitstream, coefsU[numU-1][index], 16 );
+
+ // if shift active, write the interleaved shift buffers
+ if ( bytesShifted != 0 )
+ {
+ for ( index = 0; index < numSamples; index++ )
+ BitBufferWrite( bitstream, mShiftBufferUV[index], shift );
+ }
+
+ // run the dynamic predictor with the best result
+ pc_block( mMixBufferU, mPredictorU, numSamples, coefsU[numU-1], numU, chanBits, DENSHIFT_DEFAULT );
+
+ // do lossless compression
+ set_standard_ag_params( &agParams, numSamples, numSamples );
+ status = dyn_comp( &agParams, mPredictorU, bitstream, numSamples, chanBits, &bits1 );
+ //AssertNoErr( status );
+
+
+ /* if we happened to create a compressed packet that was actually bigger than an escape packet would be,
+ chuck it and do an escape packet
+ */
+ minBits = BitBufferGetPosition( bitstream ) - BitBufferGetPosition( &startBits );
+ if ( minBits >= escapeBits )
+ {
+ *bitstream = startBits; // reset bitstream state
+ doEscape = true;
+ printf( "compressed frame too big: %u vs. %u\n", minBits, escapeBits );
+ }
+ }
+
+ if ( doEscape == true )
+ {
+ // write bitstream header and coefs
+ BitBufferWrite( bitstream, 0, 12 );
+ BitBufferWrite( bitstream, (partialFrame << 3) | 1, 4 ); // LSB = 1 means "frame not compressed"
+ if ( partialFrame )
+ BitBufferWrite( bitstream, numSamples, 32 );
+
+ // just copy the input data to the output buffer
+ switch ( mBitDepth )
+ {
+ case 16:
+ input16 = (int16_t *) inputBuffer;
+ for ( index = 0; index < (numSamples * stride); index += stride )
+ BitBufferWrite( bitstream, input16[index], 16 );
+ break;
+ case 20:
+ // convert 20-bit data to 32-bit for simplicity
+ copy20ToPredictor( (uint8_t *) inputBuffer, stride, mMixBufferU, numSamples );
+ for ( index = 0; index < numSamples; index++ )
+ BitBufferWrite( bitstream, mMixBufferU[index], 20 );
+ break;
+ case 24:
+ // convert 24-bit data to 32-bit for simplicity
+ copy24ToPredictor( (uint8_t *) inputBuffer, stride, mMixBufferU, numSamples );
+ for ( index = 0; index < numSamples; index++ )
+ BitBufferWrite( bitstream, mMixBufferU[index], 24 );
+ break;
+ case 32:
+ input32 = (int32_t *) inputBuffer;
+ for ( index = 0; index < (numSamples * stride); index += stride )
+ BitBufferWrite( bitstream, input32[index], 32 );
+ break;
+ }
+#if VERBOSE_DEBUG
+ DebugMsg( "escape!: %lu vs %lu", minBits, (numSamples * mBitDepth) );
+#endif
+ }
+
+Exit:
+ return status;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+/*
+ Encode()
+ - encode the next block of samples
+*/
+int32_t ALACEncoder::Encode(AudioFormatDescription theInputFormat, AudioFormatDescription theOutputFormat,
+ unsigned char * theReadBuffer, unsigned char * theWriteBuffer, int32_t * ioNumBytes)
+{
+ uint32_t numFrames;
+ uint32_t outputSize;
+ BitBuffer bitstream;
+ int32_t status;
+
+ numFrames = *ioNumBytes/theInputFormat.mBytesPerPacket;
+
+ // create a bit buffer structure pointing to our output buffer
+ BitBufferInit( &bitstream, theWriteBuffer, mMaxOutputBytes );
+
+ if ( theInputFormat.mChannelsPerFrame == 2 )
+ {
+ // add 3-bit frame start tag ID_CPE = channel pair & 4-bit element instance tag = 0
+ BitBufferWrite( &bitstream, ID_CPE, 3 );
+ BitBufferWrite( &bitstream, 0, 4 );
+
+ // encode stereo input buffer
+ if ( mFastMode == false )
+ status = this->EncodeStereo( &bitstream, theReadBuffer, 2, 0, numFrames );
+ else
+ status = this->EncodeStereoFast( &bitstream, theReadBuffer, 2, 0, numFrames );
+ RequireNoErr( status, goto Exit; );
+ }
+ else if ( theInputFormat.mChannelsPerFrame == 1 )
+ {
+ // add 3-bit frame start tag ID_SCE = mono channel & 4-bit element instance tag = 0
+ BitBufferWrite( &bitstream, ID_SCE, 3 );
+ BitBufferWrite( &bitstream, 0, 4 );
+
+ // encode mono input buffer
+ status = this->EncodeMono( &bitstream, theReadBuffer, 1, 0, numFrames );
+ RequireNoErr( status, goto Exit; );
+ }
+ else
+ {
+ char * inputBuffer;
+ uint32_t tag;
+ uint32_t channelIndex;
+ uint32_t inputIncrement;
+ uint8_t stereoElementTag;
+ uint8_t monoElementTag;
+ uint8_t lfeElementTag;
+
+ inputBuffer = (char *) theReadBuffer;
+ inputIncrement = ((mBitDepth + 7) / 8);
+
+ stereoElementTag = 0;
+ monoElementTag = 0;
+ lfeElementTag = 0;
+
+ for ( channelIndex = 0; channelIndex < theInputFormat.mChannelsPerFrame; )
+ {
+ tag = (sChannelMaps[theInputFormat.mChannelsPerFrame - 1] & (0x7ul << (channelIndex * 3))) >> (channelIndex * 3);
+
+ BitBufferWrite( &bitstream, tag, 3 );
+ switch ( tag )
+ {
+ case ID_SCE:
+ // mono
+ BitBufferWrite( &bitstream, monoElementTag, 4 );
+
+ status = this->EncodeMono( &bitstream, inputBuffer, theInputFormat.mChannelsPerFrame, channelIndex, numFrames );
+
+ inputBuffer += inputIncrement;
+ channelIndex++;
+ monoElementTag++;
+ break;
+
+ case ID_CPE:
+ // stereo
+ BitBufferWrite( &bitstream, stereoElementTag, 4 );
+
+ status = this->EncodeStereo( &bitstream, inputBuffer, theInputFormat.mChannelsPerFrame, channelIndex, numFrames );
+
+ inputBuffer += (inputIncrement * 2);
+ channelIndex += 2;
+ stereoElementTag++;
+ break;
+
+ case ID_LFE:
+ // LFE channel (subwoofer)
+ BitBufferWrite( &bitstream, lfeElementTag, 4 );
+
+ status = this->EncodeMono( &bitstream, inputBuffer, theInputFormat.mChannelsPerFrame, channelIndex, numFrames );
+
+ inputBuffer += inputIncrement;
+ channelIndex++;
+ lfeElementTag++;
+ break;
+
+ default:
+ printf( "That ain't right! (%u)\n", tag );
+ status = kALAC_ParamError;
+ goto Exit;
+ }
+
+ RequireNoErr( status, goto Exit; );
+ }
+ }
+
+#if VERBOSE_DEBUG
+{
+ // if there is room left in the output buffer, add some random fill data to test decoder
+ int32_t bitsLeft;
+ int32_t bytesLeft;
+
+ bitsLeft = BitBufferGetPosition( &bitstream ) - 3; // - 3 for ID_END tag
+ bytesLeft = bitstream.byteSize - ((bitsLeft + 7) / 8);
+
+ if ( (bytesLeft > 20) && ((bytesLeft & 0x4u) != 0) )
+ AddFiller( &bitstream, bytesLeft );
+}
+#endif
+
+ // add 3-bit frame end tag: ID_END
+ BitBufferWrite( &bitstream, ID_END, 3 );
+
+ // byte-align the output data
+ BitBufferByteAlign( &bitstream, true );
+
+ outputSize = BitBufferGetPosition( &bitstream ) / 8;
+ //Assert( outputSize <= mMaxOutputBytes );
+
+
+ // all good, let iTunes know what happened and remember the total number of input sample frames
+ *ioNumBytes = outputSize;
+ //mEncodedFrames += encodeMsg->numInputSamples;
+
+ // gather encoding stats
+ mTotalBytesGenerated += outputSize;
+ mMaxFrameBytes = MAX( mMaxFrameBytes, outputSize );
+
+ status = ALAC_noErr;
+
+Exit:
+ return status;
+}
+
+/*
+ Finish()
+ - drain out any leftover samples
+*/
+
+int32_t ALACEncoder::Finish()
+{
+/* // finalize bit rate statistics
+ if ( mSampleSize.numEntries != 0 )
+ mAvgBitRate = (uint32_t)( (((float)mTotalBytesGenerated * 8.0f) / (float)mSampleSize.numEntries) * ((float)mSampleRate / (float)mFrameSize) );
+ else
+ mAvgBitRate = 0;
+*/
+ return ALAC_noErr;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+/*
+ GetConfig()
+*/
+void ALACEncoder::GetConfig( ALACSpecificConfig & config )
+{
+ config.frameLength = Swap32NtoB(mFrameSize);
+ config.compatibleVersion = (uint8_t) kALACCompatibleVersion;
+ config.bitDepth = (uint8_t) mBitDepth;
+ config.pb = (uint8_t) PB0;
+ config.kb = (uint8_t) KB0;
+ config.mb = (uint8_t) MB0;
+ config.numChannels = (uint8_t) mNumChannels;
+ config.maxRun = Swap16NtoB((uint16_t) MAX_RUN_DEFAULT);
+ config.maxFrameBytes = Swap32NtoB(mMaxFrameBytes);
+ config.avgBitRate = Swap32NtoB(mAvgBitRate);
+ config.sampleRate = Swap32NtoB(mOutputSampleRate);
+}
+
+uint32_t ALACEncoder::GetMagicCookieSize(uint32_t inNumChannels)
+{
+ if (inNumChannels > 2)
+ {
+ return sizeof(ALACSpecificConfig) + kChannelAtomSize + sizeof(ALACAudioChannelLayout);
+ }
+ else
+ {
+ return sizeof(ALACSpecificConfig);
+ }
+}
+
+void ALACEncoder::GetMagicCookie(void * outCookie, uint32_t * ioSize)
+{
+ ALACSpecificConfig theConfig = {0};
+ ALACAudioChannelLayout theChannelLayout = {0};
+ uint8_t theChannelAtom[kChannelAtomSize] = {0, 0, 0, 0, 'c', 'h', 'a', 'n', 0, 0, 0, 0};
+ uint32_t theCookieSize = sizeof(ALACSpecificConfig);
+ uint8_t * theCookiePointer = (uint8_t *)outCookie;
+
+ GetConfig(theConfig);
+ if (theConfig.numChannels > 2)
+ {
+ theChannelLayout.mChannelLayoutTag = ALACChannelLayoutTags[theConfig.numChannels - 1];
+ theCookieSize += (sizeof(ALACAudioChannelLayout) + kChannelAtomSize);
+ }
+ if (*ioSize >= theCookieSize)
+ {
+ memcpy(theCookiePointer, &theConfig, sizeof(ALACSpecificConfig));
+ theChannelAtom[3] = (sizeof(ALACAudioChannelLayout) + kChannelAtomSize);
+ if (theConfig.numChannels > 2)
+ {
+ theCookiePointer += sizeof(ALACSpecificConfig);
+ memcpy(theCookiePointer, theChannelAtom, kChannelAtomSize);
+ theCookiePointer += kChannelAtomSize;
+ memcpy(theCookiePointer, &theChannelLayout, sizeof(ALACAudioChannelLayout));
+ }
+ *ioSize = theCookieSize;
+ }
+ else
+ {
+ *ioSize = 0; // no incomplete cookies
+ }
+}
+
+/*
+ InitializeEncoder()
+ - initialize the encoder component with the current config
+*/
+int32_t ALACEncoder::InitializeEncoder(AudioFormatDescription theOutputFormat)
+{
+ int32_t status;
+
+ mOutputSampleRate = theOutputFormat.mSampleRate;
+ mNumChannels = theOutputFormat.mChannelsPerFrame;
+ switch(theOutputFormat.mFormatFlags)
+ {
+ case 1:
+ mBitDepth = 16;
+ break;
+ case 2:
+ mBitDepth = 20;
+ break;
+ case 3:
+ mBitDepth = 24;
+ break;
+ case 4:
+ mBitDepth = 32;
+ break;
+ default:
+ break;
+ }
+
+ // set up default encoding parameters and state
+ // - note: mFrameSize is set in the constructor or via SetFrameSize() which must be called before this routine
+ for ( uint32_t index = 0; index < kALACMaxChannels; index++ )
+ mLastMixRes[index] = kDefaultMixRes;
+
+ // the maximum output frame size can be no bigger than (samplesPerBlock * numChannels * ((10 + sampleSize)/8) + 1)
+ // but note that this can be bigger than the input size!
+ // - since we don't yet know what our input format will be, use our max allowed sample size in the calculation
+ mMaxOutputBytes = mFrameSize * mNumChannels * ((10 + kMaxSampleSize) / 8) + 1;
+
+ // allocate mix buffers
+ mMixBufferU = (int32_t *) calloc( mFrameSize * sizeof(int32_t), 1 );
+ mMixBufferV = (int32_t *) calloc( mFrameSize * sizeof(int32_t), 1 );
+
+ // allocate dynamic predictor buffers
+ mPredictorU = (int32_t *) calloc( mFrameSize * sizeof(int32_t), 1 );
+ mPredictorV = (int32_t *) calloc( mFrameSize * sizeof(int32_t), 1 );
+
+ // allocate combined shift buffer
+ mShiftBufferUV = (uint16_t *) calloc( mFrameSize * 2 * sizeof(uint16_t),1 );
+
+ // allocate work buffer for search loop
+ mWorkBuffer = (uint8_t *) calloc( mMaxOutputBytes, 1 );
+
+ RequireAction( (mMixBufferU != nil) && (mMixBufferV != nil) &&
+ (mPredictorU != nil) && (mPredictorV != nil) &&
+ (mShiftBufferUV != nil) && (mWorkBuffer != nil ),
+ status = kALAC_MemFullError; goto Exit; );
+
+ status = ALAC_noErr;
+
+
+ // initialize coefs arrays once b/c retaining state across blocks actually improves the encode ratio
+ for ( int32_t channel = 0; channel < (int32_t)mNumChannels; channel++ )
+ {
+ for ( int32_t search = 0; search < kALACMaxSearches; search++ )
+ {
+ init_coefs( mCoefsU[channel][search], DENSHIFT_DEFAULT, kALACMaxCoefs );
+ init_coefs( mCoefsV[channel][search], DENSHIFT_DEFAULT, kALACMaxCoefs );
+ }
+ }
+
+Exit:
+ return status;
+}
+
+/*
+ GetSourceFormat()
+ - given the input format, return one of our supported formats
+*/
+void ALACEncoder::GetSourceFormat( const AudioFormatDescription * source, AudioFormatDescription * output )
+{
+ // default is 16-bit native endian
+ // - note: for float input we assume that's coming from one of our decoders (mp3, aac) so it only makes sense
+ // to encode to 16-bit since the source was lossy in the first place
+ // - note: if not a supported bit depth, find the closest supported bit depth to the input one
+ if ( (source->mFormatID != kALACFormatLinearPCM) || ((source->mFormatFlags & kALACFormatFlagIsFloat) != 0) ||
+ ( source->mBitsPerChannel <= 16 ) )
+ mBitDepth = 16;
+ else if ( source->mBitsPerChannel <= 20 )
+ mBitDepth = 20;
+ else if ( source->mBitsPerChannel <= 24 )
+ mBitDepth = 24;
+ else
+ mBitDepth = 32;
+
+ // we support 16/20/24/32-bit integer data at any sample rate and our target number of channels
+ // and sample rate were specified when we were configured
+ /*
+ MakeUncompressedAudioFormat( mNumChannels, (float) mOutputSampleRate, mBitDepth, kAudioFormatFlagsNativeIntegerPacked, output );
+ */
+}
+
+
+
+#if VERBOSE_DEBUG
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+/*
+ AddFiller()
+ - add fill and data stream elements to the bitstream to test the decoder
+*/
+static void AddFiller( BitBuffer * bits, int32_t numBytes )
+{
+ uint8_t tag;
+ uint32_t index;
+
+ // out of lameness, subtract 6 bytes to deal with header + alignment as required for fill/data elements
+ numBytes -= 6;
+ if ( numBytes <= 0 )
+ return;
+
+ // randomly pick Fill or Data Stream Element based on numBytes requested
+ tag = (numBytes & 0x8) ? ID_FIL : ID_DSE;
+
+ BitBufferWrite( bits, tag, 3 );
+ if ( tag == ID_FIL )
+ {
+ // can't write more than 269 bytes in a fill element
+ numBytes = (numBytes > 269) ? 269 : numBytes;
+
+ // fill element = 4-bit size unless >= 15 then 4-bit size + 8-bit extension size
+ if ( numBytes >= 15 )
+ {
+ uint16_t extensionSize;
+
+ BitBufferWrite( bits, 15, 4 );
+
+ // 8-bit extension count field is "extra + 1" which is weird but I didn't define the syntax
+ // - otherwise, there's no way to represent 15
+ // - for example, to really mean 15 bytes you must encode extensionSize = 1
+ // - why it's not like data stream elements I have no idea
+ extensionSize = (numBytes - 15) + 1;
+ Assert( extensionSize <= 255 );
+ BitBufferWrite( bits, extensionSize, 8 );
+ }
+ else
+ BitBufferWrite( bits, numBytes, 4 );
+
+ BitBufferWrite( bits, 0x10, 8 ); // extension_type = FILL_DATA = b0001 or'ed with fill_nibble = b0000
+ for ( index = 0; index < (numBytes - 1); index++ )
+ BitBufferWrite( bits, 0xa5, 8 ); // fill_byte = b10100101 = 0xa5
+ }
+ else
+ {
+ // can't write more than 510 bytes in a data stream element
+ numBytes = (numBytes > 510) ? 510 : numBytes;
+
+ BitBufferWrite( bits, 0, 4 ); // element instance tag
+ BitBufferWrite( bits, 1, 1 ); // byte-align flag = true
+
+ // data stream element = 8-bit size unless >= 255 then 8-bit size + 8-bit size
+ if ( numBytes >= 255 )
+ {
+ BitBufferWrite( bits, 255, 8 );
+ BitBufferWrite( bits, numBytes - 255, 8 );
+ }
+ else
+ BitBufferWrite( bits, numBytes, 8 );
+
+ BitBufferByteAlign( bits, true ); // byte-align with zeros
+
+ for ( index = 0; index < numBytes; index++ )
+ BitBufferWrite( bits, 0x5a, 8 );
+ }
+}
+
+#endif /* VERBOSE_DEBUG */
diff --git a/alac/ALACEncoder.h b/alac/ALACEncoder.h
new file mode 100644
index 0000000..0e6c689
--- /dev/null
+++ b/alac/ALACEncoder.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: ALACEncoder.h
+*/
+
+#pragma once
+
+#include
+
+#include "ALACAudioTypes.h"
+
+
+struct BitBuffer;
+
+class ALACEncoder
+{
+ public:
+ ALACEncoder();
+ virtual ~ALACEncoder();
+
+ virtual int32_t Encode(AudioFormatDescription theInputFormat, AudioFormatDescription theOutputFormat,
+ unsigned char * theReadBuffer, unsigned char * theWriteBuffer, int32_t * ioNumBytes);
+ virtual int32_t Finish( );
+
+ void SetFastMode( bool fast ) { mFastMode = fast; };
+
+ // this must be called *before* InitializeEncoder()
+ void SetFrameSize( uint32_t frameSize ) { mFrameSize = frameSize; };
+
+ void GetConfig( ALACSpecificConfig & config );
+ uint32_t GetMagicCookieSize(uint32_t inNumChannels);
+ void GetMagicCookie( void * config, uint32_t * ioSize );
+
+ virtual int32_t InitializeEncoder(AudioFormatDescription theOutputFormat);
+
+ protected:
+ virtual void GetSourceFormat( const AudioFormatDescription * source, AudioFormatDescription * output );
+
+ int32_t EncodeStereo( struct BitBuffer * bitstream, void * input, uint32_t stride, uint32_t channelIndex, uint32_t numSamples );
+ int32_t EncodeStereoFast( struct BitBuffer * bitstream, void * input, uint32_t stride, uint32_t channelIndex, uint32_t numSamples );
+ int32_t EncodeStereoEscape( struct BitBuffer * bitstream, void * input, uint32_t stride, uint32_t numSamples );
+ int32_t EncodeMono( struct BitBuffer * bitstream, void * input, uint32_t stride, uint32_t channelIndex, uint32_t numSamples );
+
+
+ // ALAC encoder parameters
+ int16_t mBitDepth;
+ bool mFastMode;
+
+ // encoding state
+ int16_t mLastMixRes[kALACMaxChannels];
+
+ // encoding buffers
+ int32_t * mMixBufferU;
+ int32_t * mMixBufferV;
+ int32_t * mPredictorU;
+ int32_t * mPredictorV;
+ uint16_t * mShiftBufferUV;
+
+ uint8_t * mWorkBuffer;
+
+ // per-channel coefficients buffers
+ int16_t mCoefsU[kALACMaxChannels][kALACMaxSearches][kALACMaxCoefs];
+ int16_t mCoefsV[kALACMaxChannels][kALACMaxSearches][kALACMaxCoefs];
+
+ // encoding statistics
+ uint32_t mTotalBytesGenerated;
+ uint32_t mAvgBitRate;
+ uint32_t mMaxFrameBytes;
+ uint32_t mFrameSize;
+ uint32_t mMaxOutputBytes;
+ uint32_t mNumChannels;
+ uint32_t mOutputSampleRate;
+};
diff --git a/alac/EndianPortable.c b/alac/EndianPortable.c
new file mode 100644
index 0000000..5a7d5b8
--- /dev/null
+++ b/alac/EndianPortable.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+//
+// EndianPortable.c
+//
+// Copyright 2011 Apple Inc. All rights reserved.
+//
+
+#include
+#include "EndianPortable.h"
+
+#define BSWAP16(x) (((x << 8) | ((x >> 8) & 0x00ff)))
+#define BSWAP32(x) (((x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | ((x >> 24) & 0x000000ff)))
+#define BSWAP64(x) ((((int64_t)x << 56) | (((int64_t)x << 40) & 0x00ff000000000000LL) | \
+ (((int64_t)x << 24) & 0x0000ff0000000000LL) | (((int64_t)x << 8) & 0x000000ff00000000LL) | \
+ (((int64_t)x >> 8) & 0x00000000ff000000LL) | (((int64_t)x >> 24) & 0x0000000000ff0000LL) | \
+ (((int64_t)x >> 40) & 0x000000000000ff00LL) | (((int64_t)x >> 56) & 0x00000000000000ffLL)))
+
+#if defined(__i386__)
+#define TARGET_RT_LITTLE_ENDIAN 1
+#elif defined(__x86_64__)
+#define TARGET_RT_LITTLE_ENDIAN 1
+#elif defined (TARGET_OS_WIN32)
+#define TARGET_RT_LITTLE_ENDIAN 1
+#endif
+
+uint16_t Swap16NtoB(uint16_t inUInt16)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ return BSWAP16(inUInt16);
+#else
+ return inUInt16;
+#endif
+}
+
+uint16_t Swap16BtoN(uint16_t inUInt16)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ return BSWAP16(inUInt16);
+#else
+ return inUInt16;
+#endif
+}
+
+uint32_t Swap32NtoB(uint32_t inUInt32)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ return BSWAP32(inUInt32);
+#else
+ return inUInt32;
+#endif
+}
+
+uint32_t Swap32BtoN(uint32_t inUInt32)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ return BSWAP32(inUInt32);
+#else
+ return inUInt32;
+#endif
+}
+
+uint64_t Swap64BtoN(uint64_t inUInt64)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ return BSWAP64(inUInt64);
+#else
+ return inUInt64;
+#endif
+}
+
+uint64_t Swap64NtoB(uint64_t inUInt64)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ return BSWAP64(inUInt64);
+#else
+ return inUInt64;
+#endif
+}
+
+float SwapFloat32BtoN(float in)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ union {
+ float f;
+ int32_t i;
+ } x;
+ x.f = in;
+ x.i = BSWAP32(x.i);
+ return x.f;
+#else
+ return in;
+#endif
+}
+
+float SwapFloat32NtoB(float in)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ union {
+ float f;
+ int32_t i;
+ } x;
+ x.f = in;
+ x.i = BSWAP32(x.i);
+ return x.f;
+#else
+ return in;
+#endif
+}
+
+double SwapFloat64BtoN(double in)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ union {
+ double f;
+ int64_t i;
+ } x;
+ x.f = in;
+ x.i = BSWAP64(x.i);
+ return x.f;
+#else
+ return in;
+#endif
+}
+
+double SwapFloat64NtoB(double in)
+{
+#if TARGET_RT_LITTLE_ENDIAN
+ union {
+ double f;
+ int64_t i;
+ } x;
+ x.f = in;
+ x.i = BSWAP64(x.i);
+ return x.f;
+#else
+ return in;
+#endif
+}
+
+void Swap16(uint16_t * inUInt16)
+{
+ *inUInt16 = BSWAP16(*inUInt16);
+}
+
+void Swap24(uint8_t * inUInt24)
+{
+ uint8_t tempVal = inUInt24[0];
+ inUInt24[0] = inUInt24[2];
+ inUInt24[2] = tempVal;
+}
+
+void Swap32(uint32_t * inUInt32)
+{
+ *inUInt32 = BSWAP32(*inUInt32);
+}
+
diff --git a/alac/EndianPortable.h b/alac/EndianPortable.h
new file mode 100644
index 0000000..f7f50a7
--- /dev/null
+++ b/alac/EndianPortable.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+//
+// EndianPortable.h
+//
+// Copyright 2011 Apple Inc. All rights reserved.
+//
+
+#ifndef _EndianPortable_h
+#define _EndianPortable_h
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+uint16_t Swap16NtoB(uint16_t inUInt16);
+uint16_t Swap16BtoN(uint16_t inUInt16);
+
+uint32_t Swap32NtoB(uint32_t inUInt32);
+uint32_t Swap32BtoN(uint32_t inUInt32);
+
+uint64_t Swap64BtoN(uint64_t inUInt64);
+uint64_t Swap64NtoB(uint64_t inUInt64);
+
+float SwapFloat32BtoN(float in);
+float SwapFloat32NtoB(float in);
+
+double SwapFloat64BtoN(double in);
+double SwapFloat64NtoB(double in);
+
+void Swap16(uint16_t * inUInt16);
+void Swap24(uint8_t * inUInt24);
+void Swap32(uint32_t * inUInt32);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/alac/ag_dec.c b/alac/ag_dec.c
new file mode 100644
index 0000000..2214c94
--- /dev/null
+++ b/alac/ag_dec.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: ag_dec.c
+
+ Contains: Adaptive Golomb decode routines.
+
+ Copyright: (c) 2001-2011 Apple, Inc.
+*/
+
+#include "aglib.h"
+#include "ALACBitUtilities.h"
+#include "ALACAudioTypes.h"
+
+#include
+#include
+#include
+#include
+#if __GNUC__ && TARGET_OS_MAC
+ #if __POWERPC__
+ #include
+ #else
+ #include
+ #endif
+#endif
+
+#define CODE_TO_LONG_MAXBITS 32
+#define N_MAX_MEAN_CLAMP 0xffff
+#define N_MEAN_CLAMP_VAL 0xffff
+#define REPORT_VAL 40
+
+#if __GNUC__
+#define ALWAYS_INLINE __attribute__((always_inline))
+#else
+#define ALWAYS_INLINE
+#endif
+
+/* And on the subject of the CodeWarrior x86 compiler and inlining, I reworked a lot of this
+ to help the compiler out. In many cases this required manual inlining or a macro. Sorry
+ if it is ugly but the performance gains are well worth it.
+ - WSK 5/19/04
+*/
+
+void set_standard_ag_params(AGParamRecPtr params, uint32_t fullwidth, uint32_t sectorwidth)
+{
+ /* Use
+ fullwidth = sectorwidth = numOfSamples, for analog 1-dimensional type-short data,
+ but use
+ fullwidth = full image width, sectorwidth = sector (patch) width
+ for such as image (2-dim.) data.
+ */
+ set_ag_params( params, MB0, PB0, KB0, fullwidth, sectorwidth, MAX_RUN_DEFAULT );
+}
+
+void set_ag_params(AGParamRecPtr params, uint32_t m, uint32_t p, uint32_t k, uint32_t f, uint32_t s, uint32_t maxrun)
+{
+ params->mb = params->mb0 = m;
+ params->pb = p;
+ params->kb = k;
+ params->wb = (1u<kb)-1;
+ params->qb = QB-params->pb;
+ params->fw = f;
+ params->sw = s;
+ params->maxrun = maxrun;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+
+// note: implementing this with some kind of "count leading zeros" assembly is a big performance win
+static inline int32_t lead( int32_t m )
+{
+ long j;
+ unsigned long c = (1ul << 31);
+
+ for(j=0; j < 32; j++)
+ {
+ if((c & m) != 0)
+ break;
+ c >>= 1;
+ }
+ return (j);
+}
+
+#define arithmin(a, b) ((a) < (b) ? (a) : (b))
+
+static inline int32_t ALWAYS_INLINE lg3a( int32_t x)
+{
+ int32_t result;
+
+ x += 3;
+ result = lead(x);
+
+ return 31 - result;
+}
+
+static inline uint32_t ALWAYS_INLINE read32bit( uint8_t * buffer )
+{
+ // embedded CPUs typically can't read unaligned 32-bit words so just read the bytes
+ uint32_t value;
+
+ value = ((uint32_t)buffer[0] << 24) | ((uint32_t)buffer[1] << 16) |
+ ((uint32_t)buffer[2] << 8) | (uint32_t)buffer[3];
+ return value;
+
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+#define get_next_fromlong(inlong, suff) ((inlong) >> (32 - (suff)))
+
+
+static inline uint32_t ALWAYS_INLINE
+getstreambits( uint8_t *in, int32_t bitoffset, int32_t numbits )
+{
+ uint32_t load1, load2;
+ uint32_t byteoffset = bitoffset / 8;
+ uint32_t result;
+
+ //Assert( numbits <= 32 );
+
+ load1 = read32bit( in + byteoffset );
+
+ if ( (numbits + (bitoffset & 0x7)) > 32)
+ {
+ int32_t load2shift;
+
+ result = load1 << (bitoffset & 0x7);
+ load2 = (uint32_t) in[byteoffset+4];
+ load2shift = (8-(numbits + (bitoffset & 0x7)-32));
+ load2 >>= load2shift;
+ result >>= (32-numbits);
+ result |= load2;
+ }
+ else
+ {
+ result = load1 >> (32-numbits-(bitoffset & 7));
+ }
+
+ // a shift of >= "the number of bits in the type of the value being shifted" results in undefined
+ // behavior so don't try to shift by 32
+ if ( numbits != (sizeof(result) * 8) )
+ result &= ~(0xfffffffful << numbits);
+
+ return result;
+}
+
+
+static inline int32_t dyn_get(unsigned char *in, uint32_t *bitPos, uint32_t m, uint32_t k)
+{
+ uint32_t tempbits = *bitPos;
+ uint32_t result;
+ uint32_t pre = 0, v;
+ uint32_t streamlong;
+
+ streamlong = read32bit( in + (tempbits >> 3) );
+ streamlong <<= (tempbits & 7);
+
+ /* find the number of bits in the prefix */
+ {
+ uint32_t notI = ~streamlong;
+ pre = lead( notI);
+ }
+
+ if(pre >= MAX_PREFIX_16)
+ {
+ pre = MAX_PREFIX_16;
+ tempbits += pre;
+ streamlong <<= pre;
+ result = get_next_fromlong(streamlong,MAX_DATATYPE_BITS_16);
+ tempbits += MAX_DATATYPE_BITS_16;
+
+ }
+ else
+ {
+ // all of the bits must fit within the long we have loaded
+ //Assert(pre+1+k <= 32);
+
+ tempbits += pre;
+ tempbits += 1;
+ streamlong <<= pre+1;
+ v = get_next_fromlong(streamlong, k);
+ tempbits += k;
+
+ result = pre*m + v-1;
+
+ if(v<2) {
+ result -= (v-1);
+ tempbits -= 1;
+ }
+ }
+
+ *bitPos = tempbits;
+ return result;
+}
+
+
+static inline int32_t dyn_get_32bit( uint8_t * in, uint32_t * bitPos, int32_t m, int32_t k, int32_t maxbits )
+{
+ uint32_t tempbits = *bitPos;
+ uint32_t v;
+ uint32_t streamlong;
+ uint32_t result;
+
+ streamlong = read32bit( in + (tempbits >> 3) );
+ streamlong <<= (tempbits & 7);
+
+ /* find the number of bits in the prefix */
+ {
+ uint32_t notI = ~streamlong;
+ result = lead( notI);
+ }
+
+ if(result >= MAX_PREFIX_32)
+ {
+ result = getstreambits(in, tempbits+MAX_PREFIX_32, maxbits);
+ tempbits += MAX_PREFIX_32 + maxbits;
+ }
+ else
+ {
+ /* all of the bits must fit within the long we have loaded*/
+ //Assert(k<=14);
+ //Assert(result=2)
+ {
+ result += (v-1);
+ tempbits += 1;
+ }
+ }
+ }
+
+ *bitPos = tempbits;
+
+ return result;
+}
+
+int32_t dyn_decomp( AGParamRecPtr params, BitBuffer * bitstream, int32_t * pc, int32_t numSamples, int32_t maxSize, uint32_t * outNumBits )
+{
+ uint8_t *in;
+ int32_t *outPtr = pc;
+ uint32_t bitPos, startPos, maxPos;
+ uint32_t j, m, k, n, c, mz;
+ int32_t del, zmode;
+ uint32_t mb;
+ uint32_t pb_local = params->pb;
+ uint32_t kb_local = params->kb;
+ uint32_t wb_local = params->wb;
+ int32_t status;
+
+ RequireAction( (bitstream != nil) && (pc != nil) && (outNumBits != nil), return kALAC_ParamError; );
+ *outNumBits = 0;
+
+ in = bitstream->cur;
+ startPos = bitstream->bitIndex;
+ maxPos = bitstream->byteSize * 8;
+ bitPos = startPos;
+
+ mb = params->mb0;
+ zmode = 0;
+
+ c = 0;
+ status = ALAC_noErr;
+
+ while (c < numSamples)
+ {
+ // bail if we've run off the end of the buffer
+ RequireAction( bitPos < maxPos, status = kALAC_ParamError; goto Exit; );
+
+ m = (mb)>>QBSHIFT;
+ k = lg3a(m);
+
+ k = arithmin(k, kb_local);
+ m = (1<> 1) * (multiplier);
+ }
+
+ *outPtr++ = del;
+
+ c++;
+
+ mb = pb_local*(n+zmode) + mb - ((pb_local*mb)>>QBSHIFT);
+
+ // update mean tracking
+ if (n > N_MAX_MEAN_CLAMP)
+ mb = N_MEAN_CLAMP_VAL;
+
+ zmode = 0;
+
+ if (((mb << MMULSHIFT) < QB) && (c < numSamples))
+ {
+ zmode = 1;
+ k = lead(mb) - BITOFF+((mb+MOFF)>>MDENSHIFT);
+ mz = ((1<= 65535)
+ zmode = 0;
+
+ mb = 0;
+ }
+ }
+
+Exit:
+ *outNumBits = (bitPos - startPos);
+ BitBufferAdvance( bitstream, *outNumBits );
+ RequireAction( bitstream->cur <= bitstream->end, status = kALAC_ParamError; );
+
+ return status;
+}
diff --git a/alac/ag_enc.c b/alac/ag_enc.c
new file mode 100644
index 0000000..2dfe999
--- /dev/null
+++ b/alac/ag_enc.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: ag_enc.c
+
+ Contains: Adaptive Golomb encode routines.
+
+ Copyright: (c) 2001-2011 Apple, Inc.
+*/
+
+#include "aglib.h"
+#include "ALACBitUtilities.h"
+#include "EndianPortable.h"
+#include "ALACAudioTypes.h"
+
+#include
+#include
+#include
+#include
+#if __GNUC__ && TARGET_OS_MAC
+ #if __POWERPC__
+ #include
+ #else
+ #include
+ #endif
+#endif
+
+#define CODE_TO_LONG_MAXBITS 32
+#define N_MAX_MEAN_CLAMP 0xffff
+#define N_MEAN_CLAMP_VAL 0xffff
+#define REPORT_VAL 40
+
+#if __GNUC__
+#define ALWAYS_INLINE __attribute__((always_inline))
+#else
+#define ALWAYS_INLINE
+#endif
+
+
+/* And on the subject of the CodeWarrior x86 compiler and inlining, I reworked a lot of this
+ to help the compiler out. In many cases this required manual inlining or a macro. Sorry
+ if it is ugly but the performance gains are well worth it.
+ - WSK 5/19/04
+*/
+
+// note: implementing this with some kind of "count leading zeros" assembly is a big performance win
+static inline int32_t lead( int32_t m )
+{
+ long j;
+ unsigned long c = (1ul << 31);
+
+ for(j=0; j < 32; j++)
+ {
+ if((c & m) != 0)
+ break;
+ c >>= 1;
+ }
+ return (j);
+}
+
+#define arithmin(a, b) ((a) < (b) ? (a) : (b))
+
+static inline int32_t ALWAYS_INLINE lg3a( int32_t x)
+{
+ int32_t result;
+
+ x += 3;
+ result = lead(x);
+
+ return 31 - result;
+}
+
+static inline int32_t ALWAYS_INLINE abs_func( int32_t a )
+{
+ // note: the CW PPC intrinsic __abs() turns into these instructions so no need to try and use it
+ int32_t isneg = a >> 31;
+ int32_t xorval = a ^ isneg;
+ int32_t result = xorval-isneg;
+
+ return result;
+}
+
+static inline uint32_t ALWAYS_INLINE read32bit( uint8_t * buffer )
+{
+ // embedded CPUs typically can't read unaligned 32-bit words so just read the bytes
+ uint32_t value;
+
+ value = ((uint32_t)buffer[0] << 24) | ((uint32_t)buffer[1] << 16) |
+ ((uint32_t)buffer[2] << 8) | (uint32_t)buffer[3];
+ return value;
+}
+
+#if PRAGMA_MARK
+#pragma mark -
+#endif
+
+static inline int32_t dyn_code(int32_t m, int32_t k, int32_t n, uint32_t *outNumBits)
+{
+ uint32_t div, mod, de;
+ uint32_t numBits;
+ uint32_t value;
+
+ //Assert( n >= 0 );
+
+ div = n/m;
+
+ if(div >= MAX_PREFIX_16)
+ {
+ numBits = MAX_PREFIX_16 + MAX_DATATYPE_BITS_16;
+ value = (((1< MAX_PREFIX_16 + MAX_DATATYPE_BITS_16)
+ {
+ numBits = MAX_PREFIX_16 + MAX_DATATYPE_BITS_16;
+ value = (((1< 25)
+ goto codeasescape;
+ }
+ else
+ {
+codeasescape:
+ numBits = MAX_PREFIX_32;
+ value = (((1<> 3));
+ uint32_t mask;
+ uint32_t curr;
+ uint32_t shift;
+
+ //Assert( numBits <= 32 );
+
+ curr = *i;
+ curr = Swap32NtoB( curr );
+
+ shift = 32 - (bitPos & 7) - numBits;
+
+ mask = ~0u >> (32 - numBits); // mask must be created in two steps to avoid compiler sequencing ambiguity
+ mask <<= shift;
+
+ value = (value << shift) & mask;
+ value |= curr & ~mask;
+
+ *i = Swap32BtoN( value );
+}
+
+
+static inline void ALWAYS_INLINE dyn_jam_noDeref_large(unsigned char *out, uint32_t bitPos, uint32_t numBits, uint32_t value)
+{
+ uint32_t * i = (uint32_t *)(out + (bitPos>>3));
+ uint32_t w;
+ uint32_t curr;
+ uint32_t mask;
+ int32_t shiftvalue = (32 - (bitPos&7) - numBits);
+
+ //Assert(numBits <= 32);
+
+ curr = *i;
+ curr = Swap32NtoB( curr );
+
+ if (shiftvalue < 0)
+ {
+ uint8_t tailbyte;
+ uint8_t *tailptr;
+
+ w = value >> -shiftvalue;
+ mask = ~0u >> -shiftvalue;
+ w |= (curr & ~mask);
+
+ tailptr = ((uint8_t *)i) + 4;
+ tailbyte = (value << ((8+shiftvalue))) & 0xff;
+ *tailptr = (uint8_t)tailbyte;
+ }
+ else
+ {
+ mask = ~0u >> (32 - numBits);
+ mask <<= shiftvalue; // mask must be created in two steps to avoid compiler sequencing ambiguity
+
+ w = (value << shiftvalue) & mask;
+ w |= curr & ~mask;
+ }
+
+ *i = Swap32BtoN( w );
+}
+
+
+int32_t dyn_comp( AGParamRecPtr params, int32_t * pc, BitBuffer * bitstream, int32_t numSamples, int32_t bitSize, uint32_t * outNumBits )
+{
+ unsigned char * out;
+ uint32_t bitPos, startPos;
+ uint32_t m, k, n, c, mz, nz;
+ uint32_t numBits;
+ uint32_t value;
+ int32_t del, zmode;
+ uint32_t overflow, overflowbits;
+ int32_t status;
+
+ // shadow the variables in params so there's not the dereferencing overhead
+ uint32_t mb, pb, kb, wb;
+ int32_t rowPos = 0;
+ int32_t rowSize = params->sw;
+ int32_t rowJump = (params->fw) - rowSize;
+ int32_t * inPtr = pc;
+
+ *outNumBits = 0;
+ RequireAction( (bitSize >= 1) && (bitSize <= 32), return kALAC_ParamError; );
+
+ out = bitstream->cur;
+ startPos = bitstream->bitIndex;
+ bitPos = startPos;
+
+ mb = params->mb = params->mb0;
+ pb = params->pb;
+ kb = params->kb;
+ wb = params->wb;
+ zmode = 0;
+
+ c=0;
+ status = ALAC_noErr;
+
+ while (c < numSamples)
+ {
+ m = mb >> QBSHIFT;
+ k = lg3a(m);
+ if ( k > kb)
+ {
+ k = kb;
+ }
+ m = (1<> 31) & 1) - zmode;
+ //Assert( 32-lead(n) <= bitSize );
+
+ if ( dyn_code_32bit(bitSize, m, k, n, &numBits, &value, &overflow, &overflowbits) )
+ {
+ dyn_jam_noDeref(out, bitPos, numBits, value);
+ bitPos += numBits;
+ dyn_jam_noDeref_large(out, bitPos, overflowbits, overflow);
+ bitPos += overflowbits;
+ }
+ else
+ {
+ dyn_jam_noDeref(out, bitPos, numBits, value);
+ bitPos += numBits;
+ }
+
+ c++;
+ if ( rowPos >= rowSize)
+ {
+ rowPos = 0;
+ inPtr += rowJump;
+ }
+
+ mb = pb * (n + zmode) + mb - ((pb *mb)>>QBSHIFT);
+
+ // update mean tracking if it's overflowed
+ if (n > N_MAX_MEAN_CLAMP)
+ mb = N_MEAN_CLAMP_VAL;
+
+ zmode = 0;
+
+ RequireAction(c <= numSamples, status = kALAC_ParamError; goto Exit; );
+
+ if (((mb << MMULSHIFT) < QB) && (c < numSamples))
+ {
+ zmode = 1;
+ nz = 0;
+
+ while(c= rowSize)
+ {
+ rowPos = 0;
+ inPtr += rowJump;
+ }
+
+ if(nz >= 65535)
+ {
+ zmode = 0;
+ break;
+ }
+ }
+
+ k = lead(mb) - BITOFF+((mb+MOFF)>>MDENSHIFT);
+ mz = ((1<
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define QBSHIFT 9
+#define QB (1<
+
+#if __GNUC__
+#define ALWAYS_INLINE __attribute__((always_inline))
+#else
+#define ALWAYS_INLINE
+#endif
+
+#if TARGET_CPU_PPC && (__MWERKS__ >= 0x3200)
+// align loops to a 16 byte boundary to make the G5 happy
+#pragma function_align 16
+#define LOOP_ALIGN asm { align 16 }
+#else
+#define LOOP_ALIGN
+#endif
+
+static inline int32_t ALWAYS_INLINE sign_of_int( int32_t i )
+{
+ int32_t negishift;
+
+ negishift = ((uint32_t)-i) >> 31;
+ return negishift | (i >> 31);
+}
+
+void unpc_block( int32_t * pc1, int32_t * out, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift )
+{
+ register int16_t a0, a1, a2, a3;
+ register int32_t b0, b1, b2, b3;
+ int32_t j, k, lim;
+ int32_t sum1, sg, sgn, top, dd;
+ int32_t * pout;
+ int32_t del, del0;
+ uint32_t chanshift = 32 - chanbits;
+ int32_t denhalf = 1<<(denshift-1);
+
+ out[0] = pc1[0];
+ if ( numactive == 0 )
+ {
+ // just copy if numactive == 0 (but don't bother if in/out pointers the same)
+ if ( (num > 1) && (pc1 != out) )
+ memcpy( &out[1], &pc1[1], (num - 1) * sizeof(int32_t) );
+ return;
+ }
+ if ( numactive == 31 )
+ {
+ // short-circuit if numactive == 31
+ int32_t prev;
+
+ /* this code is written such that the in/out buffers can be the same
+ to conserve buffer space on embedded devices like the iPod
+
+ (original code)
+ for ( j = 1; j < num; j++ )
+ del = pc1[j] + out[j-1];
+ out[j] = (del << chanshift) >> chanshift;
+ */
+ prev = out[0];
+ for ( j = 1; j < num; j++ )
+ {
+ del = pc1[j] + prev;
+ prev = (del << chanshift) >> chanshift;
+ out[j] = prev;
+ }
+ return;
+ }
+
+ for ( j = 1; j <= numactive; j++ )
+ {
+ del = pc1[j] + out[j-1];
+ out[j] = (del << chanshift) >> chanshift;
+ }
+
+ lim = numactive + 1;
+
+ if ( numactive == 4 )
+ {
+ // optimization for numactive == 4
+ register int16_t a0, a1, a2, a3;
+ register int32_t b0, b1, b2, b3;
+
+ a0 = coefs[0];
+ a1 = coefs[1];
+ a2 = coefs[2];
+ a3 = coefs[3];
+
+ for ( j = lim; j < num; j++ )
+ {
+ LOOP_ALIGN
+
+ top = out[j - lim];
+ pout = out + j - 1;
+
+ b0 = top - pout[0];
+ b1 = top - pout[-1];
+ b2 = top - pout[-2];
+ b3 = top - pout[-3];
+
+ sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3) >> denshift;
+
+ del = pc1[j];
+ del0 = del;
+ sg = sign_of_int(del);
+ del += top + sum1;
+
+ out[j] = (del << chanshift) >> chanshift;
+
+ if ( sg > 0 )
+ {
+ sgn = sign_of_int( b3 );
+ a3 -= sgn;
+ del0 -= (4 - 3) * ((sgn * b3) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b2 );
+ a2 -= sgn;
+ del0 -= (4 - 2) * ((sgn * b2) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b1 );
+ a1 -= sgn;
+ del0 -= (4 - 1) * ((sgn * b1) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ a0 -= sign_of_int( b0 );
+ }
+ else if ( sg < 0 )
+ {
+ // note: to avoid unnecessary negations, we flip the value of "sgn"
+ sgn = -sign_of_int( b3 );
+ a3 -= sgn;
+ del0 -= (4 - 3) * ((sgn * b3) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b2 );
+ a2 -= sgn;
+ del0 -= (4 - 2) * ((sgn * b2) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b1 );
+ a1 -= sgn;
+ del0 -= (4 - 1) * ((sgn * b1) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ a0 += sign_of_int( b0 );
+ }
+ }
+
+ coefs[0] = a0;
+ coefs[1] = a1;
+ coefs[2] = a2;
+ coefs[3] = a3;
+ }
+ else if ( numactive == 8 )
+ {
+ register int16_t a4, a5, a6, a7;
+ register int32_t b4, b5, b6, b7;
+
+ // optimization for numactive == 8
+ a0 = coefs[0];
+ a1 = coefs[1];
+ a2 = coefs[2];
+ a3 = coefs[3];
+ a4 = coefs[4];
+ a5 = coefs[5];
+ a6 = coefs[6];
+ a7 = coefs[7];
+
+ for ( j = lim; j < num; j++ )
+ {
+ LOOP_ALIGN
+
+ top = out[j - lim];
+ pout = out + j - 1;
+
+ b0 = top - (*pout--);
+ b1 = top - (*pout--);
+ b2 = top - (*pout--);
+ b3 = top - (*pout--);
+ b4 = top - (*pout--);
+ b5 = top - (*pout--);
+ b6 = top - (*pout--);
+ b7 = top - (*pout);
+ pout += 8;
+
+ sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3
+ - a4 * b4 - a5 * b5 - a6 * b6 - a7 * b7) >> denshift;
+
+ del = pc1[j];
+ del0 = del;
+ sg = sign_of_int(del);
+ del += top + sum1;
+
+ out[j] = (del << chanshift) >> chanshift;
+
+ if ( sg > 0 )
+ {
+ sgn = sign_of_int( b7 );
+ a7 -= sgn;
+ del0 -= 1 * ((sgn * b7) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b6 );
+ a6 -= sgn;
+ del0 -= 2 * ((sgn * b6) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b5 );
+ a5 -= sgn;
+ del0 -= 3 * ((sgn * b5) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b4 );
+ a4 -= sgn;
+ del0 -= 4 * ((sgn * b4) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b3 );
+ a3 -= sgn;
+ del0 -= 5 * ((sgn * b3) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b2 );
+ a2 -= sgn;
+ del0 -= 6 * ((sgn * b2) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b1 );
+ a1 -= sgn;
+ del0 -= 7 * ((sgn * b1) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ a0 -= sign_of_int( b0 );
+ }
+ else if ( sg < 0 )
+ {
+ // note: to avoid unnecessary negations, we flip the value of "sgn"
+ sgn = -sign_of_int( b7 );
+ a7 -= sgn;
+ del0 -= 1 * ((sgn * b7) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b6 );
+ a6 -= sgn;
+ del0 -= 2 * ((sgn * b6) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b5 );
+ a5 -= sgn;
+ del0 -= 3 * ((sgn * b5) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b4 );
+ a4 -= sgn;
+ del0 -= 4 * ((sgn * b4) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b3 );
+ a3 -= sgn;
+ del0 -= 5 * ((sgn * b3) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b2 );
+ a2 -= sgn;
+ del0 -= 6 * ((sgn * b2) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b1 );
+ a1 -= sgn;
+ del0 -= 7 * ((sgn * b1) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ a0 += sign_of_int( b0 );
+ }
+ }
+
+ coefs[0] = a0;
+ coefs[1] = a1;
+ coefs[2] = a2;
+ coefs[3] = a3;
+ coefs[4] = a4;
+ coefs[5] = a5;
+ coefs[6] = a6;
+ coefs[7] = a7;
+ }
+ else
+ {
+ // general case
+ for ( j = lim; j < num; j++ )
+ {
+ LOOP_ALIGN
+
+ sum1 = 0;
+ pout = out + j - 1;
+ top = out[j-lim];
+
+ for ( k = 0; k < numactive; k++ )
+ sum1 += coefs[k] * (pout[-k] - top);
+
+ del = pc1[j];
+ del0 = del;
+ sg = sign_of_int( del );
+ del += top + ((sum1 + denhalf) >> denshift);
+ out[j] = (del << chanshift) >> chanshift;
+
+ if ( sg > 0 )
+ {
+ for ( k = (numactive - 1); k >= 0; k-- )
+ {
+ dd = top - pout[-k];
+ sgn = sign_of_int( dd );
+ coefs[k] -= sgn;
+ del0 -= (numactive - k) * ((sgn * dd) >> denshift);
+ if ( del0 <= 0 )
+ break;
+ }
+ }
+ else if ( sg < 0 )
+ {
+ for ( k = (numactive - 1); k >= 0; k-- )
+ {
+ dd = top - pout[-k];
+ sgn = sign_of_int( dd );
+ coefs[k] += sgn;
+ del0 -= (numactive - k) * ((-sgn * dd) >> denshift);
+ if ( del0 >= 0 )
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/alac/dp_enc.c b/alac/dp_enc.c
new file mode 100644
index 0000000..869104c
--- /dev/null
+++ b/alac/dp_enc.c
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: dp_enc.c
+
+ Contains: Dynamic Predictor encode routines
+
+ Copyright: (c) 2001-2011 Apple, Inc.
+*/
+
+#include "dplib.h"
+#include
+
+#if __GNUC__
+#define ALWAYS_INLINE __attribute__((always_inline))
+#else
+#define ALWAYS_INLINE
+#endif
+
+#if TARGET_CPU_PPC && (__MWERKS__ >= 0x3200)
+// align loops to a 16 byte boundary to make the G5 happy
+#pragma function_align 16
+#define LOOP_ALIGN asm { align 16 }
+#else
+#define LOOP_ALIGN
+#endif
+
+void init_coefs( int16_t * coefs, uint32_t denshift, int32_t numPairs )
+{
+ int32_t k;
+ int32_t den = 1 << denshift;
+
+ coefs[0] = (AINIT * den) >> 4;
+ coefs[1] = (BINIT * den) >> 4;
+ coefs[2] = (CINIT * den) >> 4;
+ for ( k = 3; k < numPairs; k++ )
+ coefs[k] = 0;
+}
+
+void copy_coefs( int16_t * srcCoefs, int16_t * dstCoefs, int32_t numPairs )
+{
+ int32_t k;
+
+ for ( k = 0; k < numPairs; k++ )
+ dstCoefs[k] = srcCoefs[k];
+}
+
+static inline int32_t ALWAYS_INLINE sign_of_int( int32_t i )
+{
+ int32_t negishift;
+
+ negishift = ((uint32_t)-i) >> 31;
+ return negishift | (i >> 31);
+}
+
+void pc_block( int32_t * in, int32_t * pc1, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift )
+{
+ register int16_t a0, a1, a2, a3;
+ register int32_t b0, b1, b2, b3;
+ int32_t j, k, lim;
+ int32_t * pin;
+ int32_t sum1, dd;
+ int32_t sg, sgn;
+ int32_t top;
+ int32_t del, del0;
+ uint32_t chanshift = 32 - chanbits;
+ int32_t denhalf = 1 << (denshift - 1);
+
+ pc1[0] = in[0];
+ if ( numactive == 0 )
+ {
+ // just copy if numactive == 0 (but don't bother if in/out pointers the same)
+ if ( (num > 1) && (in != pc1) )
+ memcpy( &pc1[1], &in[1], (num - 1) * sizeof(int32_t) );
+ return;
+ }
+ if ( numactive == 31 )
+ {
+ // short-circuit if numactive == 31
+ for( j = 1; j < num; j++ )
+ {
+ del = in[j] - in[j-1];
+ pc1[j] = (del << chanshift) >> chanshift;
+ }
+ return;
+ }
+
+ for ( j = 1; j <= numactive; j++ )
+ {
+ del = in[j] - in[j-1];
+ pc1[j] = (del << chanshift) >> chanshift;
+ }
+
+ lim = numactive + 1;
+
+ if ( numactive == 4 )
+ {
+ // optimization for numactive == 4
+ a0 = coefs[0];
+ a1 = coefs[1];
+ a2 = coefs[2];
+ a3 = coefs[3];
+
+ for ( j = lim; j < num; j++ )
+ {
+ LOOP_ALIGN
+
+ top = in[j - lim];
+ pin = in + j - 1;
+
+ b0 = top - pin[0];
+ b1 = top - pin[-1];
+ b2 = top - pin[-2];
+ b3 = top - pin[-3];
+
+ sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3) >> denshift;
+
+ del = in[j] - top - sum1;
+ del = (del << chanshift) >> chanshift;
+ pc1[j] = del;
+ del0 = del;
+
+ sg = sign_of_int(del);
+ if ( sg > 0 )
+ {
+ sgn = sign_of_int( b3 );
+ a3 -= sgn;
+ del0 -= (4 - 3) * ((sgn * b3) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b2 );
+ a2 -= sgn;
+ del0 -= (4 - 2) * ((sgn * b2) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b1 );
+ a1 -= sgn;
+ del0 -= (4 - 1) * ((sgn * b1) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ a0 -= sign_of_int( b0 );
+ }
+ else if ( sg < 0 )
+ {
+ // note: to avoid unnecessary negations, we flip the value of "sgn"
+ sgn = -sign_of_int( b3 );
+ a3 -= sgn;
+ del0 -= (4 - 3) * ((sgn * b3) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b2 );
+ a2 -= sgn;
+ del0 -= (4 - 2) * ((sgn * b2) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b1 );
+ a1 -= sgn;
+ del0 -= (4 - 1) * ((sgn * b1) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ a0 += sign_of_int( b0 );
+ }
+ }
+
+ coefs[0] = a0;
+ coefs[1] = a1;
+ coefs[2] = a2;
+ coefs[3] = a3;
+ }
+ else if ( numactive == 8 )
+ {
+ // optimization for numactive == 8
+ register int16_t a4, a5, a6, a7;
+ register int32_t b4, b5, b6, b7;
+
+ a0 = coefs[0];
+ a1 = coefs[1];
+ a2 = coefs[2];
+ a3 = coefs[3];
+ a4 = coefs[4];
+ a5 = coefs[5];
+ a6 = coefs[6];
+ a7 = coefs[7];
+
+ for ( j = lim; j < num; j++ )
+ {
+ LOOP_ALIGN
+
+ top = in[j - lim];
+ pin = in + j - 1;
+
+ b0 = top - (*pin--);
+ b1 = top - (*pin--);
+ b2 = top - (*pin--);
+ b3 = top - (*pin--);
+ b4 = top - (*pin--);
+ b5 = top - (*pin--);
+ b6 = top - (*pin--);
+ b7 = top - (*pin);
+ pin += 8;
+
+ sum1 = (denhalf - a0 * b0 - a1 * b1 - a2 * b2 - a3 * b3
+ - a4 * b4 - a5 * b5 - a6 * b6 - a7 * b7) >> denshift;
+
+ del = in[j] - top - sum1;
+ del = (del << chanshift) >> chanshift;
+ pc1[j] = del;
+ del0 = del;
+
+ sg = sign_of_int(del);
+ if ( sg > 0 )
+ {
+ sgn = sign_of_int( b7 );
+ a7 -= sgn;
+ del0 -= 1 * ((sgn * b7) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b6 );
+ a6 -= sgn;
+ del0 -= 2 * ((sgn * b6) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b5 );
+ a5 -= sgn;
+ del0 -= 3 * ((sgn * b5) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b4 );
+ a4 -= sgn;
+ del0 -= 4 * ((sgn * b4) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b3 );
+ a3 -= sgn;
+ del0 -= 5 * ((sgn * b3) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b2 );
+ a2 -= sgn;
+ del0 -= 6 * ((sgn * b2) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ sgn = sign_of_int( b1 );
+ a1 -= sgn;
+ del0 -= 7 * ((sgn * b1) >> denshift);
+ if ( del0 <= 0 )
+ continue;
+
+ a0 -= sign_of_int( b0 );
+ }
+ else if ( sg < 0 )
+ {
+ // note: to avoid unnecessary negations, we flip the value of "sgn"
+ sgn = -sign_of_int( b7 );
+ a7 -= sgn;
+ del0 -= 1 * ((sgn * b7) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b6 );
+ a6 -= sgn;
+ del0 -= 2 * ((sgn * b6) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b5 );
+ a5 -= sgn;
+ del0 -= 3 * ((sgn * b5) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b4 );
+ a4 -= sgn;
+ del0 -= 4 * ((sgn * b4) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b3 );
+ a3 -= sgn;
+ del0 -= 5 * ((sgn * b3) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b2 );
+ a2 -= sgn;
+ del0 -= 6 * ((sgn * b2) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ sgn = -sign_of_int( b1 );
+ a1 -= sgn;
+ del0 -= 7 * ((sgn * b1) >> denshift);
+ if ( del0 >= 0 )
+ continue;
+
+ a0 += sign_of_int( b0 );
+ }
+ }
+
+ coefs[0] = a0;
+ coefs[1] = a1;
+ coefs[2] = a2;
+ coefs[3] = a3;
+ coefs[4] = a4;
+ coefs[5] = a5;
+ coefs[6] = a6;
+ coefs[7] = a7;
+ }
+ else
+ {
+//pc_block_general:
+ // general case
+ for ( j = lim; j < num; j++ )
+ {
+ LOOP_ALIGN
+
+ top = in[j - lim];
+ pin = in + j - 1;
+
+ sum1 = 0;
+ for ( k = 0; k < numactive; k++ )
+ sum1 -= coefs[k] * (top - pin[-k]);
+
+ del = in[j] - top - ((sum1 + denhalf) >> denshift);
+ del = (del << chanshift) >> chanshift;
+ pc1[j] = del;
+ del0 = del;
+
+ sg = sign_of_int( del );
+ if ( sg > 0 )
+ {
+ for ( k = (numactive - 1); k >= 0; k-- )
+ {
+ dd = top - pin[-k];
+ sgn = sign_of_int( dd );
+ coefs[k] -= sgn;
+ del0 -= (numactive - k) * ((sgn * dd) >> denshift);
+ if ( del0 <= 0 )
+ break;
+ }
+ }
+ else if ( sg < 0 )
+ {
+ for ( k = (numactive - 1); k >= 0; k-- )
+ {
+ dd = top - pin[-k];
+ sgn = sign_of_int( dd );
+ coefs[k] += sgn;
+ del0 -= (numactive - k) * ((-sgn * dd) >> denshift);
+ if ( del0 >= 0 )
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/alac/dplib.h b/alac/dplib.h
new file mode 100644
index 0000000..9a1ea5b
--- /dev/null
+++ b/alac/dplib.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: dplib.h
+
+ Contains: Dynamic Predictor routines
+
+ Copyright: Copyright (C) 2001-2011 Apple, Inc.
+*/
+
+#ifndef __DPLIB_H__
+#define __DPLIB_H__
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// defines
+
+#define DENSHIFT_MAX 15
+#define DENSHIFT_DEFAULT 9
+#define AINIT 38
+#define BINIT (-29)
+#define CINIT (-2)
+#define NUMCOEPAIRS 16
+
+// prototypes
+
+void init_coefs( int16_t * coefs, uint32_t denshift, int32_t numPairs );
+void copy_coefs( int16_t * srcCoefs, int16_t * dstCoefs, int32_t numPairs );
+
+// NOTE: these routines read at least "numactive" samples so the i/o buffers must be at least that big
+
+void pc_block( int32_t * in, int32_t * pc, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift );
+void unpc_block( int32_t * pc, int32_t * out, int32_t num, int16_t * coefs, int32_t numactive, uint32_t chanbits, uint32_t denshift );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DPLIB_H__ */
diff --git a/alac/matrix_dec.c b/alac/matrix_dec.c
new file mode 100644
index 0000000..b1889b9
--- /dev/null
+++ b/alac/matrix_dec.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: matrix_dec.c
+
+ Contains: ALAC mixing/matrixing decode routines.
+
+ Copyright: (c) 2004-2011 Apple, Inc.
+*/
+
+#include "matrixlib.h"
+#include "ALACAudioTypes.h"
+
+// up to 24-bit "offset" macros for the individual bytes of a 20/24-bit word
+#if TARGET_RT_BIG_ENDIAN
+ #define LBYTE 2
+ #define MBYTE 1
+ #define HBYTE 0
+#else
+ #define LBYTE 0
+ #define MBYTE 1
+ #define HBYTE 2
+#endif
+
+/*
+ There is no plain middle-side option; instead there are various mixing
+ modes including middle-side, each lossless, as embodied in the mix()
+ and unmix() functions. These functions exploit a generalized middle-side
+ transformation:
+
+ u := [(rL + (m-r)R)/m];
+ v := L - R;
+
+ where [ ] denotes integer floor. The (lossless) inverse is
+
+ L = u + v - [rV/m];
+ R = L - v;
+*/
+
+// 16-bit routines
+
+void unmix16( int32_t * u, int32_t * v, int16_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres )
+{
+ int16_t * op = out;
+ int32_t j;
+
+ if ( mixres != 0 )
+ {
+ /* matrixed stereo */
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t l, r;
+
+ l = u[j] + v[j] - ((mixres * v[j]) >> mixbits);
+ r = l - v[j];
+
+ op[0] = (int16_t) l;
+ op[1] = (int16_t) r;
+ op += stride;
+ }
+ }
+ else
+ {
+ /* Conventional separated stereo. */
+ for ( j = 0; j < numSamples; j++ )
+ {
+ op[0] = (int16_t) u[j];
+ op[1] = (int16_t) v[j];
+ op += stride;
+ }
+ }
+}
+
+// 20-bit routines
+// - the 20 bits of data are left-justified in 3 bytes of storage but right-aligned for input/output predictor buffers
+
+void unmix20( int32_t * u, int32_t * v, uint8_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres )
+{
+ uint8_t * op = out;
+ int32_t j;
+
+ if ( mixres != 0 )
+ {
+ /* matrixed stereo */
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t l, r;
+
+ l = u[j] + v[j] - ((mixres * v[j]) >> mixbits);
+ r = l - v[j];
+
+ l <<= 4;
+ r <<= 4;
+
+ op[HBYTE] = (uint8_t)((l >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((l >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((l >> 0) & 0xffu);
+ op += 3;
+
+ op[HBYTE] = (uint8_t)((r >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((r >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((r >> 0) & 0xffu);
+
+ op += (stride - 1) * 3;
+ }
+ }
+ else
+ {
+ /* Conventional separated stereo. */
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t val;
+
+ val = u[j] << 4;
+ op[HBYTE] = (uint8_t)((val >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((val >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((val >> 0) & 0xffu);
+ op += 3;
+
+ val = v[j] << 4;
+ op[HBYTE] = (uint8_t)((val >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((val >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((val >> 0) & 0xffu);
+
+ op += (stride - 1) * 3;
+ }
+ }
+}
+
+// 24-bit routines
+// - the 24 bits of data are right-justified in the input/output predictor buffers
+
+void unmix24( int32_t * u, int32_t * v, uint8_t * out, uint32_t stride, int32_t numSamples,
+ int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted )
+{
+ uint8_t * op = out;
+ int32_t shift = bytesShifted * 8;
+ int32_t l, r;
+ int32_t j, k;
+
+ if ( mixres != 0 )
+ {
+ /* matrixed stereo */
+ if ( bytesShifted != 0 )
+ {
+ for ( j = 0, k = 0; j < numSamples; j++, k += 2 )
+ {
+ l = u[j] + v[j] - ((mixres * v[j]) >> mixbits);
+ r = l - v[j];
+
+ l = (l << shift) | (uint32_t) shiftUV[k + 0];
+ r = (r << shift) | (uint32_t) shiftUV[k + 1];
+
+ op[HBYTE] = (uint8_t)((l >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((l >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((l >> 0) & 0xffu);
+ op += 3;
+
+ op[HBYTE] = (uint8_t)((r >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((r >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((r >> 0) & 0xffu);
+
+ op += (stride - 1) * 3;
+ }
+ }
+ else
+ {
+ for ( j = 0; j < numSamples; j++ )
+ {
+ l = u[j] + v[j] - ((mixres * v[j]) >> mixbits);
+ r = l - v[j];
+
+ op[HBYTE] = (uint8_t)((l >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((l >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((l >> 0) & 0xffu);
+ op += 3;
+
+ op[HBYTE] = (uint8_t)((r >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((r >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((r >> 0) & 0xffu);
+
+ op += (stride - 1) * 3;
+ }
+ }
+ }
+ else
+ {
+ /* Conventional separated stereo. */
+ if ( bytesShifted != 0 )
+ {
+ for ( j = 0, k = 0; j < numSamples; j++, k += 2 )
+ {
+ l = u[j];
+ r = v[j];
+
+ l = (l << shift) | (uint32_t) shiftUV[k + 0];
+ r = (r << shift) | (uint32_t) shiftUV[k + 1];
+
+ op[HBYTE] = (uint8_t)((l >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((l >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((l >> 0) & 0xffu);
+ op += 3;
+
+ op[HBYTE] = (uint8_t)((r >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((r >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((r >> 0) & 0xffu);
+
+ op += (stride - 1) * 3;
+ }
+ }
+ else
+ {
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t val;
+
+ val = u[j];
+ op[HBYTE] = (uint8_t)((val >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((val >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((val >> 0) & 0xffu);
+ op += 3;
+
+ val = v[j];
+ op[HBYTE] = (uint8_t)((val >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((val >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((val >> 0) & 0xffu);
+
+ op += (stride - 1) * 3;
+ }
+ }
+ }
+}
+
+// 32-bit routines
+// - note that these really expect the internal data width to be < 32 but the arrays are 32-bit
+// - otherwise, the calculations might overflow into the 33rd bit and be lost
+// - therefore, these routines deal with the specified "unused lower" bytes in the "shift" buffers
+
+void unmix32( int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples,
+ int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted )
+{
+ int32_t * op = out;
+ int32_t shift = bytesShifted * 8;
+ int32_t l, r;
+ int32_t j, k;
+
+ if ( mixres != 0 )
+ {
+ //Assert( bytesShifted != 0 );
+
+ /* matrixed stereo with shift */
+ for ( j = 0, k = 0; j < numSamples; j++, k += 2 )
+ {
+ int32_t lt, rt;
+
+ lt = u[j];
+ rt = v[j];
+
+ l = lt + rt - ((mixres * rt) >> mixbits);
+ r = l - rt;
+
+ op[0] = (l << shift) | (uint32_t) shiftUV[k + 0];
+ op[1] = (r << shift) | (uint32_t) shiftUV[k + 1];
+ op += stride;
+ }
+ }
+ else
+ {
+ if ( bytesShifted == 0 )
+ {
+ /* interleaving w/o shift */
+ for ( j = 0; j < numSamples; j++ )
+ {
+ op[0] = u[j];
+ op[1] = v[j];
+ op += stride;
+ }
+ }
+ else
+ {
+ /* interleaving with shift */
+ for ( j = 0, k = 0; j < numSamples; j++, k += 2 )
+ {
+ op[0] = (u[j] << shift) | (uint32_t) shiftUV[k + 0];
+ op[1] = (v[j] << shift) | (uint32_t) shiftUV[k + 1];
+ op += stride;
+ }
+ }
+ }
+}
+
+// 20/24-bit <-> 32-bit helper routines (not really matrixing but convenient to put here)
+
+void copyPredictorTo24( int32_t * in, uint8_t * out, uint32_t stride, int32_t numSamples )
+{
+ uint8_t * op = out;
+ int32_t j;
+
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t val = in[j];
+
+ op[HBYTE] = (uint8_t)((val >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((val >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((val >> 0) & 0xffu);
+ op += (stride * 3);
+ }
+}
+
+void copyPredictorTo24Shift( int32_t * in, uint16_t * shift, uint8_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted )
+{
+ uint8_t * op = out;
+ int32_t shiftVal = bytesShifted * 8;
+ int32_t j;
+
+ //Assert( bytesShifted != 0 );
+
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t val = in[j];
+
+ val = (val << shiftVal) | (uint32_t) shift[j];
+
+ op[HBYTE] = (uint8_t)((val >> 16) & 0xffu);
+ op[MBYTE] = (uint8_t)((val >> 8) & 0xffu);
+ op[LBYTE] = (uint8_t)((val >> 0) & 0xffu);
+ op += (stride * 3);
+ }
+}
+
+void copyPredictorTo20( int32_t * in, uint8_t * out, uint32_t stride, int32_t numSamples )
+{
+ uint8_t * op = out;
+ int32_t j;
+
+ // 32-bit predictor values are right-aligned but 20-bit output values should be left-aligned
+ // in the 24-bit output buffer
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t val = in[j];
+
+ op[HBYTE] = (uint8_t)((val >> 12) & 0xffu);
+ op[MBYTE] = (uint8_t)((val >> 4) & 0xffu);
+ op[LBYTE] = (uint8_t)((val << 4) & 0xffu);
+ op += (stride * 3);
+ }
+}
+
+void copyPredictorTo32( int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples )
+{
+ int32_t i, j;
+
+ // this is only a subroutine to abstract the "iPod can only output 16-bit data" problem
+ for ( i = 0, j = 0; i < numSamples; i++, j += stride )
+ out[j] = in[i];
+}
+
+void copyPredictorTo32Shift( int32_t * in, uint16_t * shift, int32_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted )
+{
+ int32_t * op = out;
+ uint32_t shiftVal = bytesShifted * 8;
+ int32_t j;
+
+ //Assert( bytesShifted != 0 );
+
+ // this is only a subroutine to abstract the "iPod can only output 16-bit data" problem
+ for ( j = 0; j < numSamples; j++ )
+ {
+ op[0] = (in[j] << shiftVal) | (uint32_t) shift[j];
+ op += stride;
+ }
+}
diff --git a/alac/matrix_enc.c b/alac/matrix_enc.c
new file mode 100644
index 0000000..e194330
--- /dev/null
+++ b/alac/matrix_enc.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: matrix_enc.c
+
+ Contains: ALAC mixing/matrixing encode routines.
+
+ Copyright: (c) 2004-2011 Apple, Inc.
+*/
+
+#include "matrixlib.h"
+#include "ALACAudioTypes.h"
+
+// up to 24-bit "offset" macros for the individual bytes of a 20/24-bit word
+#if TARGET_RT_BIG_ENDIAN
+ #define LBYTE 2
+ #define MBYTE 1
+ #define HBYTE 0
+#else
+ #define LBYTE 0
+ #define MBYTE 1
+ #define HBYTE 2
+#endif
+
+/*
+ There is no plain middle-side option; instead there are various mixing
+ modes including middle-side, each lossless, as embodied in the mix()
+ and unmix() functions. These functions exploit a generalized middle-side
+ transformation:
+
+ u := [(rL + (m-r)R)/m];
+ v := L - R;
+
+ where [ ] denotes integer floor. The (lossless) inverse is
+
+ L = u + v - [rV/m];
+ R = L - v;
+*/
+
+// 16-bit routines
+
+void mix16( int16_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres )
+{
+ int16_t * ip = in;
+ int32_t j;
+
+ if ( mixres != 0 )
+ {
+ int32_t mod = 1 << mixbits;
+ int32_t m2;
+
+ /* matrixed stereo */
+ m2 = mod - mixres;
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t l, r;
+
+ l = (int32_t) ip[0];
+ r = (int32_t) ip[1];
+ ip += stride;
+ u[j] = (mixres * l + m2 * r) >> mixbits;
+ v[j] = l - r;
+ }
+ }
+ else
+ {
+ /* Conventional separated stereo. */
+ for ( j = 0; j < numSamples; j++ )
+ {
+ u[j] = (int32_t) ip[0];
+ v[j] = (int32_t) ip[1];
+ ip += stride;
+ }
+ }
+}
+
+// 20-bit routines
+// - the 20 bits of data are left-justified in 3 bytes of storage but right-aligned for input/output predictor buffers
+
+void mix20( uint8_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres )
+{
+ int32_t l, r;
+ uint8_t * ip = in;
+ int32_t j;
+
+ if ( mixres != 0 )
+ {
+ /* matrixed stereo */
+ int32_t mod = 1 << mixbits;
+ int32_t m2 = mod - mixres;
+
+ for ( j = 0; j < numSamples; j++ )
+ {
+ l = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ l = (l << 8) >> 12;
+ ip += 3;
+
+ r = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ r = (r << 8) >> 12;
+ ip += (stride - 1) * 3;
+
+ u[j] = (mixres * l + m2 * r) >> mixbits;
+ v[j] = l - r;
+ }
+ }
+ else
+ {
+ /* Conventional separated stereo. */
+ for ( j = 0; j < numSamples; j++ )
+ {
+ l = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ u[j] = (l << 8) >> 12;
+ ip += 3;
+
+ r = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ v[j] = (r << 8) >> 12;
+ ip += (stride - 1) * 3;
+ }
+ }
+}
+
+// 24-bit routines
+// - the 24 bits of data are right-justified in the input/output predictor buffers
+
+void mix24( uint8_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples,
+ int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted )
+{
+ int32_t l, r;
+ uint8_t * ip = in;
+ int32_t shift = bytesShifted * 8;
+ uint32_t mask = (1ul << shift) - 1;
+ int32_t j, k;
+
+ if ( mixres != 0 )
+ {
+ /* matrixed stereo */
+ int32_t mod = 1 << mixbits;
+ int32_t m2 = mod - mixres;
+
+ if ( bytesShifted != 0 )
+ {
+ for ( j = 0, k = 0; j < numSamples; j++, k += 2 )
+ {
+ l = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ l = (l << 8) >> 8;
+ ip += 3;
+
+ r = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ r = (r << 8) >> 8;
+ ip += (stride - 1) * 3;
+
+ shiftUV[k + 0] = (uint16_t)(l & mask);
+ shiftUV[k + 1] = (uint16_t)(r & mask);
+
+ l >>= shift;
+ r >>= shift;
+
+ u[j] = (mixres * l + m2 * r) >> mixbits;
+ v[j] = l - r;
+ }
+ }
+ else
+ {
+ for ( j = 0; j < numSamples; j++ )
+ {
+ l = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ l = (l << 8) >> 8;
+ ip += 3;
+
+ r = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ r = (r << 8) >> 8;
+ ip += (stride - 1) * 3;
+
+ u[j] = (mixres * l + m2 * r) >> mixbits;
+ v[j] = l - r;
+ }
+ }
+ }
+ else
+ {
+ /* Conventional separated stereo. */
+ if ( bytesShifted != 0 )
+ {
+ for ( j = 0, k = 0; j < numSamples; j++, k += 2 )
+ {
+ l = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ l = (l << 8) >> 8;
+ ip += 3;
+
+ r = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ r = (r << 8) >> 8;
+ ip += (stride - 1) * 3;
+
+ shiftUV[k + 0] = (uint16_t)(l & mask);
+ shiftUV[k + 1] = (uint16_t)(r & mask);
+
+ l >>= shift;
+ r >>= shift;
+
+ u[j] = l;
+ v[j] = r;
+ }
+ }
+ else
+ {
+ for ( j = 0; j < numSamples; j++ )
+ {
+ l = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ u[j] = (l << 8) >> 8;
+ ip += 3;
+
+ r = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ v[j] = (r << 8) >> 8;
+ ip += (stride - 1) * 3;
+ }
+ }
+ }
+}
+
+// 32-bit routines
+// - note that these really expect the internal data width to be < 32 but the arrays are 32-bit
+// - otherwise, the calculations might overflow into the 33rd bit and be lost
+// - therefore, these routines deal with the specified "unused lower" bytes in the "shift" buffers
+
+void mix32( int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples,
+ int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted )
+{
+ int32_t * ip = in;
+ int32_t shift = bytesShifted * 8;
+ uint32_t mask = (1ul << shift) - 1;
+ int32_t l, r;
+ int32_t j, k;
+
+ if ( mixres != 0 )
+ {
+ int32_t mod = 1 << mixbits;
+ int32_t m2;
+
+ //Assert( bytesShifted != 0 );
+
+ /* matrixed stereo with shift */
+ m2 = mod - mixres;
+ for ( j = 0, k = 0; j < numSamples; j++, k += 2 )
+ {
+ l = ip[0];
+ r = ip[1];
+ ip += stride;
+
+ shiftUV[k + 0] = (uint16_t)(l & mask);
+ shiftUV[k + 1] = (uint16_t)(r & mask);
+
+ l >>= shift;
+ r >>= shift;
+
+ u[j] = (mixres * l + m2 * r) >> mixbits;
+ v[j] = l - r;
+ }
+ }
+ else
+ {
+ if ( bytesShifted == 0 )
+ {
+ /* de-interleaving w/o shift */
+ for ( j = 0; j < numSamples; j++ )
+ {
+ u[j] = ip[0];
+ v[j] = ip[1];
+ ip += stride;
+ }
+ }
+ else
+ {
+ /* de-interleaving with shift */
+ for ( j = 0, k = 0; j < numSamples; j++, k += 2 )
+ {
+ l = ip[0];
+ r = ip[1];
+ ip += stride;
+
+ shiftUV[k + 0] = (uint16_t)(l & mask);
+ shiftUV[k + 1] = (uint16_t)(r & mask);
+
+ l >>= shift;
+ r >>= shift;
+
+ u[j] = l;
+ v[j] = r;
+ }
+ }
+ }
+}
+
+// 20/24-bit <-> 32-bit helper routines (not really matrixing but convenient to put here)
+
+void copy20ToPredictor( uint8_t * in, uint32_t stride, int32_t * out, int32_t numSamples )
+{
+ uint8_t * ip = in;
+ int32_t j;
+
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t val;
+
+ // 20-bit values are left-aligned in the 24-bit input buffer but right-aligned in the 32-bit output buffer
+ val = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ out[j] = (val << 8) >> 12;
+ ip += stride * 3;
+ }
+}
+
+void copy24ToPredictor( uint8_t * in, uint32_t stride, int32_t * out, int32_t numSamples )
+{
+ uint8_t * ip = in;
+ int32_t j;
+
+ for ( j = 0; j < numSamples; j++ )
+ {
+ int32_t val;
+
+ val = (int32_t)( ((uint32_t)ip[HBYTE] << 16) | ((uint32_t)ip[MBYTE] << 8) | (uint32_t)ip[LBYTE] );
+ out[j] = (val << 8) >> 8;
+ ip += stride * 3;
+ }
+}
diff --git a/alac/matrixlib.h b/alac/matrixlib.h
new file mode 100644
index 0000000..0a4f371
--- /dev/null
+++ b/alac/matrixlib.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011 Apple Inc. All rights reserved.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_START@
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @APPLE_APACHE_LICENSE_HEADER_END@
+ */
+
+/*
+ File: matrixlib.h
+
+ Contains: ALAC mixing/matrixing routines to/from 32-bit predictor buffers.
+
+ Copyright: Copyright (C) 2004 to 2011 Apple, Inc.
+*/
+
+#ifndef __MATRIXLIB_H
+#define __MATRIXLIB_H
+
+#pragma once
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// 16-bit routines
+void mix16( int16_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres );
+void unmix16( int32_t * u, int32_t * v, int16_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres );
+
+// 20-bit routines
+void mix20( uint8_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples, int32_t mixbits, int32_t mixres );
+void unmix20( int32_t * u, int32_t * v, uint8_t * out, uint32_t stride, int32_t numSamples, int32_t mixbits, int32_t mixres );
+
+// 24-bit routines
+// - 24-bit data sometimes compresses better by shifting off the bottom byte so these routines deal with
+// the specified "unused lower bytes" in the combined "shift" buffer
+void mix24( uint8_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples,
+ int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted );
+void unmix24( int32_t * u, int32_t * v, uint8_t * out, uint32_t stride, int32_t numSamples,
+ int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted );
+
+// 32-bit routines
+// - note that these really expect the internal data width to be < 32-bit but the arrays are 32-bit
+// - otherwise, the calculations might overflow into the 33rd bit and be lost
+// - therefore, these routines deal with the specified "unused lower" bytes in the combined "shift" buffer
+void mix32( int32_t * in, uint32_t stride, int32_t * u, int32_t * v, int32_t numSamples,
+ int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted );
+void unmix32( int32_t * u, int32_t * v, int32_t * out, uint32_t stride, int32_t numSamples,
+ int32_t mixbits, int32_t mixres, uint16_t * shiftUV, int32_t bytesShifted );
+
+// 20/24/32-bit <-> 32-bit helper routines (not really matrixing but convenient to put here)
+void copy20ToPredictor( uint8_t * in, uint32_t stride, int32_t * out, int32_t numSamples );
+void copy24ToPredictor( uint8_t * in, uint32_t stride, int32_t * out, int32_t numSamples );
+
+void copyPredictorTo24( int32_t * in, uint8_t * out, uint32_t stride, int32_t numSamples );
+void copyPredictorTo24Shift( int32_t * in, uint16_t * shift, uint8_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted );
+void copyPredictorTo20( int32_t * in, uint8_t * out, uint32_t stride, int32_t numSamples );
+
+void copyPredictorTo32( int32_t * in, int32_t * out, uint32_t stride, int32_t numSamples );
+void copyPredictorTo32Shift( int32_t * in, uint16_t * shift, int32_t * out, uint32_t stride, int32_t numSamples, int32_t bytesShifted );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MATRIXLIB_H */
diff --git a/build/.wafpickle-7 b/build/.wafpickle-7
new file mode 100644
index 0000000..61a7e28
Binary files /dev/null and b/build/.wafpickle-7 differ
diff --git a/build/c4che/Release.cache.py b/build/c4che/Release.cache.py
new file mode 100644
index 0000000..2656c08
--- /dev/null
+++ b/build/c4che/Release.cache.py
@@ -0,0 +1,48 @@
+AR = '/usr/bin/ar'
+ARFLAGS = 'rcs'
+CCFLAGS = ['-g']
+CCFLAGS_MACBUNDLE = ['-fPIC']
+CCFLAGS_NODE = ['-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64']
+CC_VERSION = ('4', '2', '1')
+COMPILER_CXX = 'g++'
+CPP = '/usr/bin/cpp'
+CPPFLAGS_NODE = ['-D_GNU_SOURCE']
+CPPPATH_NODE = '/usr/local/Cellar/node/0.6.11/include/node'
+CPPPATH_ST = '-I%s'
+CXX = ['/usr/bin/g++']
+CXXDEFINES_ST = '-D%s'
+CXXFLAGS = ['-g']
+CXXFLAGS_DEBUG = ['-g']
+CXXFLAGS_NODE = ['-D_LARGEFILE_SOURCE', '-D_FILE_OFFSET_BITS=64']
+CXXFLAGS_RELEASE = ['-O2']
+CXXLNK_SRC_F = ''
+CXXLNK_TGT_F = ['-o', '']
+CXX_NAME = 'gcc'
+CXX_SRC_F = ''
+CXX_TGT_F = ['-c', '-o', '']
+DEST_CPU = 'x86_64'
+DEST_OS = 'darwin'
+FULLSTATIC_MARKER = '-static'
+LIBDIR = '/Users/laurent/.node_libraries'
+LIBPATH_NODE = '/usr/local/Cellar/node/0.6.11/lib'
+LIBPATH_ST = '-L%s'
+LIB_ST = '-l%s'
+LINKFLAGS_MACBUNDLE = ['-bundle', '-undefined', 'dynamic_lookup']
+LINK_CXX = ['/usr/bin/g++']
+NODE_PATH = '/Users/laurent/.node_libraries'
+PREFIX = '/usr/local'
+PREFIX_NODE = '/usr/local/Cellar/node/0.6.11'
+RANLIB = '/usr/bin/ranlib'
+RPATH_ST = '-Wl,-rpath,%s'
+SHLIB_MARKER = ''
+SONAME_ST = ''
+STATICLIBPATH_ST = '-L%s'
+STATICLIB_MARKER = ''
+STATICLIB_ST = '-l%s'
+macbundle_PATTERN = '%s.bundle'
+program_PATTERN = '%s'
+shlib_CXXFLAGS = ['-fPIC', '-compatibility_version', '1', '-current_version', '1']
+shlib_LINKFLAGS = ['-dynamiclib']
+shlib_PATTERN = 'lib%s.dylib'
+staticlib_LINKFLAGS = []
+staticlib_PATTERN = 'lib%s.a'
diff --git a/build/c4che/build.config.py b/build/c4che/build.config.py
new file mode 100644
index 0000000..55cf008
--- /dev/null
+++ b/build/c4che/build.config.py
@@ -0,0 +1,2 @@
+version = 0x105016
+tools = [{'tool': 'ar', 'tooldir': None, 'funs': None}, {'tool': 'cxx', 'tooldir': None, 'funs': None}, {'tool': 'gxx', 'tooldir': None, 'funs': None}, {'tool': 'compiler_cxx', 'tooldir': None, 'funs': None}, {'tool': 'osx', 'tooldir': None, 'funs': None}, {'tool': 'node_addon', 'tooldir': None, 'funs': None}]
diff --git a/build/config.log b/build/config.log
new file mode 100644
index 0000000..6506eb3
--- /dev/null
+++ b/build/config.log
@@ -0,0 +1,36 @@
+# project noname (0.0.1) configured on Wed Feb 29 20:36:47 2012 by
+# waf 1.5.16 (abi 7, python 20701f0 on darwin)
+# using /usr/local/bin/node-waf configure build
+#
+
+----------------------------------------
+Checking for program g++ or c++
+ find program=['g++', 'c++'] paths=[] var='CXX'
+ -> '/usr/bin/g++'
+
+----------------------------------------
+Checking for program cpp
+ find program=['cpp'] paths=[] var='CPP'
+ -> '/usr/bin/cpp'
+
+----------------------------------------
+Checking for program ar
+ find program=['ar'] paths=[] var='AR'
+ -> '/usr/bin/ar'
+
+----------------------------------------
+Checking for program ranlib
+ find program=['ranlib'] paths=[] var='RANLIB'
+ -> '/usr/bin/ranlib'
+
+----------------------------------------
+Checking for g++
+ok
+
+----------------------------------------
+Checking for node path
+not found
+
+----------------------------------------
+Checking for node prefix
+ok /usr/local/Cellar/node/0.6.11
diff --git a/examples/play_ffmpeg.js b/examples/play_ffmpeg.js
new file mode 100644
index 0000000..51d9e29
--- /dev/null
+++ b/examples/play_ffmpeg.js
@@ -0,0 +1,57 @@
+var airtunes = require('../lib/'),
+ spawn = require('child_process').spawn,
+ argv = require('optimist')
+ .usage('Usage: $0 --host [host] --port [num] --ffmpeg [path] --file [path] --volume [num] --password [string]')
+ .default('port', 5000)
+ .default('volume', 50)
+ .default('ffmpeg', '/usr/local/bin/ffmpeg')
+ .default('file', './sample.mp3')
+ .demand(['host'])
+ .argv;
+
+console.log('adding device: ' + argv.host + ':' + argv.port);
+airtunes.add(argv);
+
+// when the device is online, spawn ffmpeg to transcode the file
+airtunes.on('device', function(key, status, desc) {
+ console.log('device ' + key + ' status: ' + status + ' ' + desc);
+
+ if(status !== 'playing')
+ process.exit(1);
+
+ var ffmpeg = spawn(argv.ffmpeg, [
+ '-i', argv.file,
+ '-f', 's16le', // PCM 16bits, little-endian
+ '-ar', '44100', // Sampling rate
+ '-ac', 2, // Stereo
+ 'pipe:1' // Output on stdout
+ ]);
+
+ // pipe data to AirTunes
+ ffmpeg.stdout.pipe(airtunes);
+
+ // detect if ffmpeg was not spawned correctly
+ ffmpeg.stderr.setEncoding('utf8');
+ ffmpeg.stderr.on('data', function(data) {
+ if(/^execvp\(\)/.test(data)) {
+ console.log('failed to start ' + argv.ffmpeg);
+ process.exit(1);
+ }
+ });
+});
+
+// monitor buffer events
+airtunes.on('buffer', function(status) {
+ console.log('buffer ' + status);
+
+ // after the playback ends, give some time to AirTunes devices
+ if(status === 'end') {
+ console.log('playback ended, waiting for AirTunes devices');
+ setTimeout(function() {
+ airtunes.stopAll(function() {
+ console.log('end');
+ process.exit();
+ });
+ }, 2000);
+ }
+});
diff --git a/examples/play_stdin.js b/examples/play_stdin.js
new file mode 100644
index 0000000..b2321a7
--- /dev/null
+++ b/examples/play_stdin.js
@@ -0,0 +1,40 @@
+var airtunes = require('../lib/'),
+ argv = require('optimist')
+ .usage('Usage: $0 --host [host] --port [num] --volume [num] --password [string]')
+ .default('port', 5000)
+ .default('volume', 50)
+ .demand(['host'])
+ .argv;
+
+console.log('pipe PCM data to play over AirTunes');
+console.log('example: cat ./sample.pcm | node play_stdin.js --host \n');
+
+console.log('adding device: ' + argv.host + ':' + argv.port);
+airtunes.add(argv);
+
+// when the device is online, spawn ffmpeg to transcode the file
+airtunes.on('device', function(key, status, desc) {
+ console.log('device ' + key + ' status: ' + status + (desc ? ' ' + desc : ''));
+
+ if(status !== 'playing')
+ process.exit(1);
+
+ process.stdin.pipe(airtunes);
+ process.stdin.resume();
+});
+
+// monitor buffer events
+airtunes.on('buffer', function(status) {
+ console.log('buffer ' + status);
+
+ // after the playback ends, give some time to AirTunes devices
+ if(status === 'end') {
+ console.log('playback ended, waiting for AirTunes devices');
+ setTimeout(function() {
+ airtunes.stopAll(function() {
+ console.log('end');
+ process.exit();
+ });
+ }, 2000);
+ }
+});
diff --git a/examples/sample.mp3 b/examples/sample.mp3
new file mode 100644
index 0000000..5460855
Binary files /dev/null and b/examples/sample.mp3 differ
diff --git a/examples/sample.pcm b/examples/sample.pcm
new file mode 100644
index 0000000..b1b54cb
Binary files /dev/null and b/examples/sample.pcm differ
diff --git a/examples/scan_airtunes.js b/examples/scan_airtunes.js
new file mode 100644
index 0000000..00b0e58
--- /dev/null
+++ b/examples/scan_airtunes.js
@@ -0,0 +1,36 @@
+var util = require('util'),
+ os = require('os'),
+ exec = require('child_process').exec;
+
+if(os.platform() !== 'darwin') {
+ console.error('Sorry, this utility only works with OS X.');
+ process.exit(1);
+}
+
+exec('mDNS -B _raop._tcp', {
+ timeout: 300
+}, function(error, stdout) {
+ var devices = [];
+ stdout.split('\n').forEach(function(line) {
+ var res = /_raop\._tcp\.\s+([^@]+)@(.*)/.exec(line);
+ if(!res)
+ return;
+
+ devices.push({
+ mac: res[1],
+ name: res[2]
+ });
+ });
+
+ devices.forEach(function(dev) {
+ exec('mDNS -L "' + dev.mac + '@' + dev.name + '" _raop._tcp local', {
+ timeout: 300
+ }, function(error, stdout) {
+ var res = /Service can be reached at\s+(\S*)\s+:(\d+)/.exec(stdout);
+ if(!res)
+ console.log(dev.name + ' no match');
+ else
+ console.log(dev.name + ' ' + res[1] + ':' + res[2]);
+ });
+ });
+});
diff --git a/lib/audio_out.js b/lib/audio_out.js
new file mode 100644
index 0000000..3923aca
--- /dev/null
+++ b/lib/audio_out.js
@@ -0,0 +1,101 @@
+var events = require('events'),
+ util = require('util'),
+ config = require('./config.js'),
+ nu = require('./num_util.js'),
+ circularBuffer = require('./circular_buffer.js'),
+ bindings = require('../build/Release/bindings');
+
+function AudioOut() {
+ events.EventEmitter.call(this);
+
+ this.lastSeq = -1;
+ this.encoder = bindings.newEncoder();
+ this.needAirTunesPacket = false;
+}
+
+util.inherits(AudioOut, events.EventEmitter);
+
+AudioOut.prototype.init = function(devices) {
+ var self = this;
+ config.rtp_time_ref = new Date().getTime();
+
+ devices.on('airtunes_devices', function(hasAirTunes) {
+ // only compute AirTunes packets when we have AirTunes devices
+ self.needAirTunesPacket = hasAirTunes;
+ });
+
+ devices.on('need_sync', function() {
+ // a sync is forced when a new remote device is added
+ self.emit('need_sync', self.lastSeq);
+ });
+
+ function sendPacket(seq) {
+ // sendPacket prepares packets for all devices
+ var packet = circularBuffer.readPacket();
+ packet.seq = seq;
+
+ if(self.needAirTunesPacket) {
+ makeAirTunesPacket(packet, seq);
+
+ if(seq % config.sync_period == 0)
+ self.emit('need_sync', seq);
+ }
+
+ self.emit('packet', packet);
+ packet.release();
+ }
+
+ function makeAirTunesPacket(packet, seq) {
+ packet.alac = pcmToAlac(self.encoder, packet.pcm)
+ var airTunesData = new Buffer(12 + packet.alac.length);
+
+ if(seq == 0)
+ airTunesData.writeUInt16BE(0x80e0, 0);
+ else
+ airTunesData.writeUInt16BE(0x8060, 0);
+
+ airTunesData.writeUInt16BE(nu.low16(seq), 2);
+
+ var timestamp = nu.low32(seq*config.frames_per_packet + 2*config.sampling_rate);
+ airTunesData.writeUInt32BE(timestamp, 4);
+ airTunesData.writeUInt32BE(config.device_magic, 8);
+
+ packet.alac.copy(airTunesData, 12);
+ packet.airTunes = airTunesData;
+ packet.timestamp = timestamp;
+ }
+
+ function syncAudio() {
+ /*
+ * Each time syncAudio() runs, a burst of packet is sent.
+ * Increasing config.stream_latency lowers CPU usage but increases the size of the burst.
+ * If the burst size exceeds the UDP windows size (which we do not know), packets are lost.
+ */
+ var elapsed = new Date().getTime() - config.rtp_time_ref;
+
+ /*
+ * currentSeq is the # of the packet we should be sending now. We have some packets to catch-up
+ * since syncAudio is not always running.
+ */
+ var currentSeq = Math.floor(elapsed*config.sampling_rate/(config.frames_per_packet*1000));
+
+ for(var i = self.lastSeq + 1; i <= currentSeq; i++)
+ sendPacket(i);
+
+ self.lastSeq = currentSeq;
+
+ // reschedule ourselves later
+ setTimeout(syncAudio, config.stream_latency);
+ }
+
+ syncAudio();
+}
+
+function pcmToAlac(encoder, pcmData) {
+ var alacData = new Buffer(config.packet_size + 8);
+ var alacSize = bindings.encodePacket(encoder, pcmData, alacData, pcmData.length);
+
+ return alacData.slice(0, alacSize);
+}
+
+module.exports = new AudioOut();
diff --git a/lib/circular_buffer.js b/lib/circular_buffer.js
new file mode 100644
index 0000000..613a642
--- /dev/null
+++ b/lib/circular_buffer.js
@@ -0,0 +1,130 @@
+var Stream = require('stream'),
+ util = require('util'),
+ config = require('./config.js'),
+ packetPool = require('./packet_pool.js');
+
+var WAITING = 0,
+ FILLING = 1,
+ NORMAL = 2,
+ DRAINING = 3,
+ ENDING = 4,
+ ENDED = 5;
+
+function CircularBuffer(packetsInBuffer, packetSize) {
+ Stream.call(this);
+
+ this.maxSize = packetsInBuffer*packetSize;
+ this.packetSize = packetSize;
+ this.writable = true;
+ this.muted = false;
+ this.buffers = [];
+ this.currentSize = 0;
+ this.status = WAITING;
+}
+
+util.inherits(CircularBuffer, Stream);
+
+CircularBuffer.prototype.write = function(chunk) {
+ this.buffers.push(chunk);
+ this.currentSize += chunk.length;
+
+ if(this.status === ENDING || this.status === ENDED)
+ throw new Error('Cannot write in buffer after closing it');
+
+ if(this.status === WAITING) {
+ // notify when we receive the first chunk
+ this.emit('status', 'buffering');
+ this.status = FILLING;
+ }
+
+ if(this.status === FILLING && this.currentSize > this.maxSize/2) {
+ this.status = NORMAL;
+ this.emit('status', 'playing');
+ }
+
+ if(this.currentSize >= this.maxSize) {
+ this.status = DRAINING;
+ return false;
+ } else {
+ return true;
+ }
+};
+
+CircularBuffer.prototype.readPacket = function() {
+ var packet = packetPool.getPacket();
+
+ // play silence until buffer is filled enough
+ if(this.status !== ENDING && this.status !== ENDED &&
+ (this.status === FILLING || this.currentSize < this.packetSize)) {
+ packet.pcm.fill(0);
+
+ if(this.status !== FILLING && this.status !== WAITING) {
+ this.status = FILLING;
+ this.emit('status', 'buffering');
+ }
+ } else {
+ var offset = 0, remaining = this.packetSize;
+
+ // fill a whole packet with data
+ while(remaining > 0) {
+ // pad packet with silence if buffer is empty
+ if(this.buffers.length === 0) {
+ packet.pcm.fill(0, offset);
+ remaining = 0;
+ break;
+ }
+
+ var first = this.buffers[0];
+
+ if(first.length <= remaining) {
+ // pop the whole buffer from the queue
+ first.copy(packet.pcm, offset);
+ offset += first.length;
+ remaining -= first.length;
+ this.buffers.shift();
+ } else {
+ // first buffer contains enough data to fill a packet: slice it
+ first.copy(packet.pcm, offset, 0, remaining);
+ this.buffers[0] = first.slice(remaining);
+ remaining = 0;
+ offset += remaining;
+ }
+ }
+
+ this.currentSize -= this.packetSize;
+
+ // emit 'end' only once
+ if(this.status === ENDING && this.currentSize <= 0) {
+ this.status = ENDED;
+ this.currentSize = 0;
+ this.emit('status', 'end');
+ }
+
+ // notify that the buffer now has enough room if needed
+ if(this.status === DRAINING && this.currentSize < this.maxSize/2) {
+ this.status = NORMAL;
+ this.emit('drain');
+ }
+ }
+
+ if(this.muted)
+ packet.pcm.fill(0);
+
+ return packet;
+};
+
+CircularBuffer.prototype.end = function() {
+ // flush the buffer if it was filling
+ if(this.status === FILLING)
+ this.emit('status', 'playing');
+
+ this.status = ENDING;
+};
+
+CircularBuffer.prototype.reset = function() {
+ this.buffers = [];
+ this.currentSize = 0;
+ this.status = WAITING;
+};
+
+module.exports = new CircularBuffer(config.packets_in_buffer, config.packet_size);
diff --git a/lib/config.js b/lib/config.js
new file mode 100644
index 0000000..8996e63
--- /dev/null
+++ b/lib/config.js
@@ -0,0 +1,25 @@
+var nu = require('./num_util.js');
+
+var Config = {
+ user_agent: 'Radioline/1.4.0',
+ udp_default_port: 6002, // preferred starting port in AirTunes v2
+ frames_per_packet: 352, // samples per frames in ALAC packets
+ channels_per_frame: 2, // always stereo in AirTunes v2
+ bits_per_channel: 16, // -> 2 bytes per channel
+ packet_size: 352*2*2, // frames*channels*bytes
+ packets_in_buffer: 100, // increase this buffer protects against network issues
+ coreaudio_min_level: 5, // if CoreAudio's internal buffer drops too much, inject some silence to raise it
+ coreaudio_check_period: 2000, // CoreAudio buffer level check period
+ coreaudio_preload: 1408*50, // ~0.5s of silence pushed to CoreAudio to avoid draining AudioQueue
+ sampling_rate: 44100, // fixed by AirTunes v2
+ sync_period: 126, // UDP sync packets are sent to all AirTunes devices regularly
+ stream_latency: 50, // audio UDP packets are flushed in bursts periodically
+ rtsp_timeout: 5000, // RTSP servers are considered gone if no reply is received before the timeout
+ rtp_time_ref: 0,
+ device_magic: nu.randomInt(9),
+ ntp_epoch: 0x83aa7e80,
+ iv_base64: "ePRBLI0XN5ArFaaz7ncNZw",
+ rsa_aeskey_base64: "VjVbxWcmYgbBbhwBNlCh3K0CMNtWoB844BuiHGUJT51zQS7SDpMnlbBIobsKbfEJ3SCgWHRXjYWf7VQWRYtEcfx7ejA8xDIk5PSBYTvXP5dU2QoGrSBv0leDS6uxlEWuxBq3lIxCxpWO2YswHYKJBt06Uz9P2Fq2hDUwl3qOQ8oXb0OateTKtfXEwHJMprkhsJsGDrIc5W5NJFMAo6zCiM9bGSDeH2nvTlyW6bfI/Q0v0cDGUNeY3ut6fsoafRkfpCwYId+bg3diJh+uzw5htHDyZ2sN+BFYHzEfo8iv4KDxzeya9llqg6fRNQ8d5YjpvTnoeEQ9ye9ivjkBjcAfVw"
+};
+
+module.exports = Config;
diff --git a/lib/device_airtunes.js b/lib/device_airtunes.js
new file mode 100644
index 0000000..d202f75
--- /dev/null
+++ b/lib/device_airtunes.js
@@ -0,0 +1,128 @@
+var dgram = require('dgram'),
+ events = require('events'),
+ util = require('util'),
+ RTSP = require('./rtsp.js'),
+ udpServers = require('./udp_servers.js'),
+ audioOut = require('./audio_out.js');
+
+function AirTunesDevice(options) {
+ events.EventEmitter.call(this);
+
+ if(!options.host)
+ throw new Error('host is mandatory');
+
+ this.type = 'airtunes';
+ this.key = options.host + ':' + options.port;
+ this.host = options.host;
+ this.port = options.port || 5000;
+ this.rtsp = new RTSP.Client(options.volume || 50, options.password || null);
+ this.audioCallback = null;
+
+ this.status = null;
+ this.statusDescription = null;
+};
+
+util.inherits(AirTunesDevice, events.EventEmitter);
+
+AirTunesDevice.prototype.start = function() {
+ var self = this;
+ this.audioSocket = dgram.createSocket('udp4');
+
+ // Wait until timing and control ports are chosen. We need them in RTSP handshake.
+ udpServers.once('ports', function(err) {
+ if(err) {
+ self.reportStatus('error', 'udp_ports ' + err.code);
+ return;
+ }
+
+ self.doHandshake();
+ });
+
+ udpServers.bind();
+};
+
+AirTunesDevice.prototype.doHandshake = function() {
+ var self = this;
+
+ this.rtsp.on('remote_ports', function(setup) {
+ self.audioLatency = setup.audioLatency;
+ self.serverPort = setup.server_port;
+ self.controlPort = setup.control_port;
+ self.timingPort = setup.timing_port;
+ });
+
+ this.rtsp.on('ready', function(err, msg) {
+ if(err) {
+ self.reportStatus(err, msg);
+ return;
+ }
+
+ self.relayAudio();
+ });
+
+ this.rtsp.on('end', function(type) {
+ if(type === 'stopped')
+ self.cleanup('stopped');
+ else
+ self.cleanup('error', type);
+ });
+
+ this.rtsp.startHandshake(this.host, this.port);
+};
+
+AirTunesDevice.prototype.relayAudio = function() {
+ var self = this;
+ this.reportStatus('playing');
+
+ this.audioCallback = function(packet) {
+ packet.retain();
+ self.audioSocket.send(
+ packet.airTunes, 0, packet.airTunes.length,
+ self.serverPort, self.host,
+ function() { packet.release(); }
+ );
+ };
+
+ audioOut.on('packet', this.audioCallback);
+};
+
+AirTunesDevice.prototype.onSyncNeeded = function(seq) {
+ udpServers.sendControlSync(seq, this);
+};
+
+AirTunesDevice.prototype.cleanup = function(status, desc) {
+ this.audioSocket = null;
+ this.reportStatus(status, desc);
+
+ if(this.audioCallback) {
+ audioOut.removeListener('packet', this.audioCallback);
+ this.audioCallback = null;
+ }
+
+ this.removeAllListeners();
+};
+
+AirTunesDevice.prototype.stop = function(cb) {
+ this.rtsp.once('teardown', function() {
+ if(cb)
+ cb();
+ });
+
+ this.rtsp.teardown();
+};
+
+AirTunesDevice.prototype.reportStatus = function(status, description) {
+ if(typeof status === 'string') {
+ this.status = status;
+ this.statusDescription = description;
+ }
+
+ if(typeof this.status === 'string')
+ this.emit('status', this.status, this.description);
+};
+
+AirTunesDevice.prototype.setVolume = function(volume) {
+ this.rtsp.setVolume(volume);
+};
+
+module.exports = AirTunesDevice;
diff --git a/lib/device_null.js b/lib/device_null.js
new file mode 100644
index 0000000..43febd1
--- /dev/null
+++ b/lib/device_null.js
@@ -0,0 +1,39 @@
+var events = require('events'),
+ util = require('util'),
+ audioOut = require('./audio_out.js');
+
+function NullDevice() {
+ events.EventEmitter.call(this);
+ this.type = 'null';
+ this.key = 'null';
+};
+
+util.inherits(NullDevice, events.EventEmitter);
+
+NullDevice.prototype.start = function() {
+ this.emit('status', 'playing');
+
+ this.audioCallback = function(packet) {
+ // don't do anything
+ };
+
+ audioOut.on('packet', this.audioCallback);
+};
+
+NullDevice.prototype.setVolume = function(volume) {
+};
+
+NullDevice.prototype.reportStatus = function() {
+ // we're always playing
+ this.emit('status', 'playing');
+};
+
+NullDevice.prototype.stop = function(cb) {
+ if(this.audioCallback)
+ audioOut.removeListener('packet', this.audioCallback);
+
+ if(cb)
+ cb();
+};
+
+module.exports = NullDevice;
diff --git a/lib/devices.js b/lib/devices.js
new file mode 100644
index 0000000..4daf0b8
--- /dev/null
+++ b/lib/devices.js
@@ -0,0 +1,141 @@
+var events = require('events'),
+ util = require('util'),
+ async = require('async'),
+ audioOut = require('./audio_out.js'),
+ config = require('./config.js');
+
+function Devices() {
+ events.EventEmitter.call(this);
+
+ this.source = null;
+ this.devices = {};
+ this.hasAirTunes = false;
+};
+
+util.inherits(Devices, events.EventEmitter);
+
+Devices.prototype.init = function() {
+ var self = this;
+ audioOut.on('need_sync', function(seq) {
+ // relay to all devices
+ self.forEach(function(dev) {
+ if(dev.onSyncNeeded)
+ dev.onSyncNeeded(seq);
+ });
+ });
+};
+
+Devices.prototype.forEach = function(it) {
+ for(var i in this.devices) {
+ if(!this.devices.hasOwnProperty(i))
+ continue;
+
+ it(this.devices[i], i);
+ }
+};
+
+Devices.prototype.add = function(dev, syncWhenReady) {
+ var self = this;
+
+ if(!dev)
+ return null;
+
+ if(syncWhenReady === undefined)
+ syncWhenReady = true;
+
+ var previousDev = this.devices[dev.key];
+
+ if(previousDev) {
+ // if device is already in the pool, just report its existing status.
+ previousDev.reportStatus();
+
+ return previousDev.key;
+ }
+
+ this.devices[dev.key] = dev;
+
+ dev.on('status', function(status, arg) {
+ self.emit('status', dev.key, status, arg);
+
+ if(status === 'error') {
+ delete self.devices[dev.key];
+ self.checkAirTunesDevices();
+ }
+
+ if(syncWhenReady && status === 'playing') {
+ self.emit('need_sync');
+ syncWhenReady = false;
+ }
+ });
+
+ this.checkAirTunesDevices();
+ dev.start();
+
+ return dev.key;
+};
+
+Devices.prototype.stop = function(key, cb) {
+ var dev = this.devices[key];
+
+ if(!dev) {
+ this.emit('status', key, 'error', 'not_found');
+
+ return;
+ }
+
+ delete this.devices[key];
+ dev.stop(cb);
+ this.checkAirTunesDevices();
+};
+
+Devices.prototype.setVolume = function(key, volume) {
+ var dev = this.devices[key];
+
+ if(!dev) {
+ this.emit('status', key, 'error', 'not_found');
+
+ return;
+ }
+
+ dev.setVolume(volume);
+};
+
+Devices.prototype.stopAll = function(cb) {
+ async.forEach(
+ this.devices,
+ function(dev, cb) {
+ dev.stop(cb);
+ },
+ function() {
+ this.devices = {};
+ cb();
+ }
+ );
+};
+
+Devices.prototype.checkAirTunesDevices = function() {
+ var newHasAirTunes = false;
+
+ for(var host in this.devices) {
+ if(!this.devices.hasOwnProperty(host))
+ continue;
+
+ if(this.devices[host].type === 'airtunes') {
+ newHasAirTunes = true;
+ break;
+ }
+ }
+
+ if(newHasAirTunes !== this.hasAirTunes) {
+ this.emit('airtunes_devices', newHasAirTunes);
+
+ this.forEach(function(dev) {
+ if(dev.setHasAirTunes)
+ dev.setHasAirTunes(newHasAirTunes);
+ });
+ }
+
+ this.hasAirTunes = newHasAirTunes;
+};
+
+module.exports = new Devices();
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..57018b3
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,65 @@
+var Stream = require('stream'),
+ util = require('util'),
+ AirTunesDevice = require('./device_airtunes.js');
+ devices = require('./devices.js'),
+ circularBuffer = require('./circular_buffer.js'),
+ audioOut = require('./audio_out.js'),
+ udpServers = require('./udp_servers.js');
+
+function AirTunes() {
+ var self = this;
+
+ Stream.call(this);
+
+ devices.init();
+ devices.on('status', function(key, status, desc) {
+ self.emit('device', key, status, desc);
+ });
+
+ circularBuffer.on('status', function(status) {
+ self.emit('buffer', status);
+ });
+
+ audioOut.init(devices);
+ udpServers.init(devices);
+
+ circularBuffer.on('drain', function() {
+ self.emit('drain');
+ });
+
+ circularBuffer.on('error', function(err) {
+ self.emit('error', err);
+ });
+
+ this.writable = true;
+}
+
+util.inherits(AirTunes, Stream);
+
+AirTunes.prototype.add = function(options) {
+ var device = new AirTunesDevice(options);
+
+ return devices.add(device);
+};
+
+AirTunes.prototype.stop = function(deviceKey, cb) {
+ return devices.stop(deviceKey, cb);
+};
+
+AirTunes.prototype.stopAll = function(cb) {
+ devices.stopAll(cb);
+};
+
+AirTunes.prototype.setVolume = function(deviceKey, volume) {
+ devices.setVolume(deviceKey, volume);
+};
+
+AirTunes.prototype.write = function(data) {
+ return circularBuffer.write(data);
+};
+
+AirTunes.prototype.end = function() {
+ circularBuffer.end();
+};
+
+module.exports = new AirTunes();
diff --git a/lib/ntp.js b/lib/ntp.js
new file mode 100644
index 0000000..3646369
--- /dev/null
+++ b/lib/ntp.js
@@ -0,0 +1,22 @@
+var config = require('./config.js');
+
+function NTP() {
+ this.timeRef = new Date().getTime() - config.ntp_epoch*1000;
+}
+
+NTP.prototype.timestamp = function() {
+ var time = new Date().getTime() - this.timeRef;
+ var sec = Math.floor(time/1000);
+
+ var msec = time - sec*1000;
+ var ntp_msec = Math.floor(msec*4294967.296);
+
+ var ts = new Buffer(8);
+
+ ts.writeUInt32BE(sec, 0);
+ ts.writeUInt32BE(ntp_msec, 4);
+
+ return ts;
+}
+
+module.exports = new NTP();
diff --git a/lib/num_util.js b/lib/num_util.js
new file mode 100644
index 0000000..7146b37
--- /dev/null
+++ b/lib/num_util.js
@@ -0,0 +1,21 @@
+var crypto = require('crypto');
+
+exports.randomHex = function(n) {
+ return crypto.randomBytes(n).toString('hex');
+}
+
+exports.randomBase64 = function(n) {
+ return crypto.randomBytes(n).toString('base64').replace("=", "");
+}
+
+exports.randomInt = function(n) {
+ return Math.floor(Math.random()*Math.pow(10, n));
+}
+
+exports.low16 = function(i) {
+ return i % 65536;
+}
+
+exports.low32 = function(i) {
+ return i % 4294967296;
+}
diff --git a/lib/packet_pool.js b/lib/packet_pool.js
new file mode 100644
index 0000000..507604a
--- /dev/null
+++ b/lib/packet_pool.js
@@ -0,0 +1,42 @@
+var config = require('./config.js');
+
+function PacketPool() {
+ this.pool = [];
+}
+
+PacketPool.prototype.getPacket = function() {
+ var packet = this.pool.shift();
+
+ if(!packet) {
+ packet = new Packet(this);
+ } else
+ packet.retain();
+
+ return packet;
+};
+
+PacketPool.prototype.release = function(packet) {
+ this.pool.push(packet);
+};
+
+function Packet(pool) {
+ this.pool = pool;
+ this.ref = 1;
+ this.seq = null;
+ this.pcm = new Buffer(config.packet_size);
+}
+
+Packet.prototype.retain = function() {
+ this.ref++;
+};
+
+Packet.prototype.release = function() {
+ this.ref--;
+
+ if(this.ref === 0) {
+ this.seq = null;
+ this.pool.release(this);
+ }
+};
+
+module.exports = new PacketPool();
diff --git a/lib/rtsp.js b/lib/rtsp.js
new file mode 100644
index 0000000..371014e
--- /dev/null
+++ b/lib/rtsp.js
@@ -0,0 +1,389 @@
+var net = require('net'),
+ crypto = require('crypto'),
+ events = require('events'),
+ util = require('util'),
+ config = require('./config.js'),
+ udpServers = require('./udp_servers.js'),
+ audioOut = require('./audio_out.js'),
+ nu = require('./num_util.js');
+
+var OPTIONS = 0,
+ ANNOUNCE = 1,
+ SETUP = 2,
+ RECORD = 3,
+ SETVOLUME = 4,
+ PLAYING = 5,
+ TEARDOWN = 6,
+ CLOSED = 7;
+
+function Client(volume, password) {
+ events.EventEmitter.call(this);
+
+ this.status = OPTIONS;
+ this.socket = null;
+ this.cseq = 0;
+ this.announceId = null;
+ this.activeRemote = nu.randomInt(9);
+ this.dacpId = nu.randomHex(8);
+ this.session = null;
+ this.timeout = null;
+ this.volume = volume;
+ this.password = password;
+ this.passwordTried = false;
+}
+
+util.inherits(Client, events.EventEmitter);
+
+exports.Client = Client;
+
+Client.prototype.startHandshake = function(host, port) {
+ var self = this;
+
+ this.startTimeout();
+
+ this.socket = net.connect(port, host, function() {
+ self.clearTimeout();
+ self.sendNextRequest();
+ });
+
+ var blob = '';
+ this.socket.on('data', function(data) {
+ self.clearTimeout();
+
+ /*
+ * I wish I could use node's HTTP parser for this...
+ * I assume that all responses have empty bodies.
+ */
+ data = data.toString();
+ var endIndex = data.indexOf('\r\n\r\n');
+
+ if(endIndex < 0) {
+ blob += data;
+ return;
+ }
+
+ endIndex += 4; // the end of \r\n\r\n
+
+ blob += data.substring(0, endIndex);
+ self.processData(blob);
+
+ blob = data.substring(endIndex);
+ });
+
+ this.socket.on('error', function(err) {
+ self.socket = null;
+
+ if(err.code === 'ECONNREFUSED')
+ self.cleanup('connection_refused');
+ else
+ self.cleanup('rtsp_socket', err.code);
+ });
+
+ this.socket.on('end', function() {
+ self.cleanup('disconnected');
+ });
+};
+
+Client.prototype.startTimeout = function() {
+ var self = this;
+
+ this.timeout = setTimeout(function() {
+ self.cleanup('timeout');
+ }, config.rtsp_timeout);
+};
+
+Client.prototype.clearTimeout = function() {
+ if(this.timeout !== null) {
+ clearTimeout(this.timeout);
+ this.timeout = null;
+ }
+};
+
+Client.prototype.teardown = function() {
+ if(this.status === CLOSED) {
+ this.emit('end', 'stopped');
+ return;
+ }
+
+ this.status = TEARDOWN;
+ this.sendNextRequest();
+};
+
+Client.prototype.setVolume = function(volume) {
+ if(this.status !== PLAYING)
+ return;
+
+ this.volume = volume;
+ this.status = SETVOLUME;
+ this.sendNextRequest();
+};
+
+Client.prototype.nextCSeq = function() {
+ this.cseq += 1;
+
+ return this.cseq;
+};
+
+Client.prototype.cleanup = function(type, msg) {
+ this.emit('end', type, msg);
+ this.status = CLOSED;
+ this.removeAllListeners();
+
+ if(this.timeout) {
+ clearTimeout(this.timeout);
+ this.timeout = null;
+ }
+
+ if(this.socket) {
+ this.socket.destroy();
+ this.socket = null;
+ }
+};
+
+function parseResponse(blob) {
+ var response = {}, lines = blob.split('\r\n');
+
+ var codeRes = /(\w+)\/(\S+) (\d+) (.*)/.exec(lines[0]);
+ if(!codeRes) {
+ response.code = 599;
+ response.status = 'UNEXPECTED ' + lines[0];
+
+ return response;
+ }
+
+ response.code = parseInt(codeRes[3]);
+ response.status = codeRes[4];
+
+ var headers = {};
+ lines.slice(1).forEach(function(line) {
+ var res = /([^:]+): (.*)/.exec(line);
+
+ if(!res)
+ return;
+
+ headers[res[1]] = res[2];
+ });
+
+ response.headers = headers;
+
+ return response;
+}
+
+function md5(str) {
+ var md5sum = crypto.createHash('md5');
+ md5sum.update(str);
+
+ return md5sum.digest('hex').toUpperCase();
+}
+
+Client.prototype.makeHead = function(method, uri, di) {
+ var head = method + ' ' + uri + ' RTSP/1.0\r\n' +
+ 'CSeq: ' + this.nextCSeq() + '\r\n' +
+ 'User-Agent: ' + config.user_agent + '\r\n' +
+ 'DACP-ID: ' + this.dacpId + '\r\n' +
+ 'Client-Instance: ' + this.dacpId + '\r\n' +
+ (this.session ? 'Session: ' + this.session + '\r\n' : '') +
+ 'Active-Remote: ' + this.activeRemote + '\r\n';
+
+ if(di) {
+ var ha1 = md5(di.username + ':' + di.realm + ':' + di.password);
+ var ha2 = md5(method + ':' + uri);
+ var diResponse = md5(ha1 + ':' + di.nonce + ':' + ha2);
+
+ head += 'Authorization: Digest ' +
+ 'username="' + di.username + '", ' +
+ 'realm="' + di.realm + '", ' +
+ 'nonce="' + di.nonce + '", ' +
+ 'uri="' + uri + '", ' +
+ 'response="' + diResponse + '"\r\n';
+ }
+
+ return head;
+}
+
+Client.prototype.makeHeadWithURL = function(method, digestInfo) {
+ return this.makeHead(method, 'rtsp://' + this.socket.address().address + '/' + this.announceId, digestInfo);
+}
+
+Client.prototype.sendNextRequest = function(di) {
+ var request = '', body = '';
+
+ switch(this.status) {
+ case OPTIONS:
+ request += this.makeHead('OPTIONS', '*', di);
+ request += 'Apple-Challenge: SdX9kFJVxgKVMFof/Znj4Q\r\n\r\n';
+ break;
+
+ case ANNOUNCE:
+ this.announceId = nu.randomInt(8);
+
+ body =
+ 'v=0\r\n' +
+ 'o=iTunes ' + this.announceId +' 0 IN IP4 ' + this.socket.address().address + '\r\n' +
+ 's=iTunes\r\n' +
+ 'c=IN IP4 ' + this.socket.address().address + '\r\n' +
+ 't=0 0\r\n' +
+ 'm=audio 0 RTP/AVP 96\r\n' +
+ 'a=rtpmap:96 AppleLossless\r\n' +
+ 'a=fmtp:96 352 0 16 40 10 14 2 255 0 0 44100\r\n' +
+ 'a=rsaaeskey:' + config.rsa_aeskey_base64 + '\r\n' +
+ 'a=aesiv:' + config.iv_base64 + '\r\n';
+
+ request += this.makeHeadWithURL('ANNOUNCE', di);
+ request +=
+ 'Content-Type: application/sdp\r\n' +
+ 'Content-Length: ' + body.length + '\r\n\r\n';
+
+ request += body;
+ break;
+
+ case SETUP:
+ request += this.makeHeadWithURL('SETUP', di);
+ request +=
+ 'Transport: RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;' +
+ 'control_port=' + udpServers.control.port + ';' +
+ 'timing_port=' + udpServers.timing.port + '\r\n\r\n';
+ break;
+
+ case RECORD:
+ var nextSeq = audioOut.lastSeq + 1;
+ var rtpSyncTime = nextSeq*config.frames_per_packet + 2*config.sampling_rate;
+ request += this.makeHeadWithURL('RECORD', di);
+ request +=
+ 'RTP-Info: seq=' + nextSeq + ';rtptime=' + rtpSyncTime + '\r\n' +
+ 'Range: npt=0-\r\n\r\n';
+ break;
+
+ case SETVOLUME:
+ var attenuation =
+ this.volume === 0.0 ?
+ -144.0 :
+ (-30.0)*(100 - this.volume)/100.0;
+
+ body = 'volume: ' + attenuation + '\r\n';
+
+ request += this.makeHeadWithURL('SET_PARAMETER', di);
+ request +=
+ 'Content-Type: text/parameters\r\n' +
+ 'Content-Length: ' + body.length + '\r\n\r\n';
+
+ request += body;
+ break;
+
+ case TEARDOWN:
+ this.socket.end(this.makeHead('TEARDOWN', '', di) + '\r\n');
+ this.cleanup('stopped');
+ // return here since the socket is closed
+ return;
+
+ default:
+ return;
+ }
+
+ this.startTimeout();
+ this.socket.write(request);
+};
+
+Client.prototype.parsePorts = function(headers) {
+ function parsePort(name, transport) {
+ var re = new RegExp(name + '=(\\d+)');
+ var res = re.exec(transport);
+
+ return res ? parseInt(res[1]) : null;
+ }
+
+ var transport = headers['Transport'],
+ portConfig = { audioLatency: parseInt(headers['Audio-Latency']) },
+ names = ['server_port', 'control_port', 'timing_port'];
+
+ for(var i = 0; i < names.length; i++) {
+ var name = names[i];
+ var port = parsePort(name, transport);
+
+ if(!port) {
+ this.cleanup('parse_ports', transport);
+ return false;
+ } else
+ portConfig[name] = port;
+ }
+
+ this.emit('remote_ports', portConfig);
+
+ return true;
+}
+
+function parseAuthenticate(auth, field) {
+ var re = new RegExp(field + '="([^"]+)"'),
+ res = re.exec(auth);
+
+ return res ? res[1] : null;
+}
+
+Client.prototype.processData = function(blob) {
+ var response = parseResponse(blob),
+ headers = response.headers;
+
+ if(response.code === 401) {
+ if(!this.password) {
+ this.cleanup('no_password');
+ return;
+ }
+
+ if(this.passwordTried) {
+ this.cleanup('bad_password');
+ return;
+ } else
+ this.passwordTried = true;
+
+ var auth = headers['WWW-Authenticate'];
+ var di = {
+ realm: parseAuthenticate(auth, 'realm'),
+ nonce: parseAuthenticate(auth, 'nonce'),
+ username: 'Radioline',
+ password: this.password
+ };
+
+ this.sendNextRequest(di);
+ return;
+ }
+
+ if(response.code === 453) {
+ this.cleanup('busy');
+ return;
+ }
+
+ if(response.code !== 200) {
+ this.cleanup(response.status);
+ return;
+ }
+
+ // password was accepted (or not needed)
+ this.passwordTried = false;
+
+ switch(this.status) {
+ case OPTIONS:
+ this.status = ANNOUNCE;
+ break;
+
+ case ANNOUNCE:
+ this.status = SETUP;
+ break;
+
+ case SETUP:
+ this.status = RECORD;
+ this.session = headers['Session'];
+ this.parsePorts(headers);
+ break;
+
+ case RECORD:
+ this.status = SETVOLUME;
+ this.emit('ready');
+ break;
+
+ case SETVOLUME:
+ this.status = PLAYING;
+ break;
+ }
+
+ this.sendNextRequest();
+}
diff --git a/lib/udp_servers.js b/lib/udp_servers.js
new file mode 100644
index 0000000..9b5b0cf
--- /dev/null
+++ b/lib/udp_servers.js
@@ -0,0 +1,177 @@
+var dgram = require('dgram'),
+ events = require('events'),
+ util = require('util'),
+ async = require('async'),
+ config = require('./config.js'),
+ nu = require('./num_util.js'),
+ ntp = require('./ntp.js');
+
+var UNBOUND = 0,
+ BINDING = 1,
+ BOUND = 2;
+
+function UDPServers() {
+ events.EventEmitter.call(this);
+
+ this.status = UNBOUND;
+
+ this.control = {
+ socket: null,
+ port: null,
+ name: 'control'
+ };
+
+ this.timing = {
+ socket: null,
+ port: null,
+ name: 'timing'
+ };
+}
+
+util.inherits(UDPServers, events.EventEmitter);
+
+UDPServers.prototype.init = function(devices) {
+ var self = this;
+
+ devices.on('airtunes_devices', function(hasAirTunes) {
+ // open UDP servers only when we have AirTunes devices
+ if(!hasAirTunes)
+ self.close();
+ });
+};
+
+UDPServers.prototype.bind = function() {
+ var self = this;
+
+ switch(this.status) {
+ case BOUND:
+ process.nextTick(function() {
+ self.emit('ports', null, this.control, this.timing);
+ });
+ return;
+
+ case BINDING:
+ return;
+ }
+
+ var self = this;
+ this.status = BINDING;
+
+ // Timing socket
+ this.timing.socket = dgram.createSocket('udp4');
+
+ this.timing.socket.on('message', function(msg, rinfo) {
+ var ts1 = msg.readUInt32BE(24);
+ var ts2 = msg.readUInt32BE(28);
+
+ var reply = new Buffer(32);
+ reply.writeUInt16BE(0x80d3, 0);
+ reply.writeUInt16BE(0x0007, 2);
+ reply.writeUInt32BE(0x00000000, 4);
+
+ reply.writeUInt32BE(ts1, 8);
+ reply.writeUInt32BE(ts2, 12);
+
+ var ntpTime = ntp.timestamp();
+
+ ntpTime.copy(reply, 16);
+ ntpTime.copy(reply, 24);
+
+ self.timing.socket.send(
+ reply, 0, reply.length,
+ rinfo.port, rinfo.address
+ );
+ });
+
+ // Control socket
+ this.control.socket = dgram.createSocket('udp4');
+
+ this.control.socket.on('message', function(msg, rinfo) {
+ var serverSeq = msg.readUInt16BE(2);
+ var missedSeq = msg.readUInt16BE(4);
+ var count = msg.readUInt16BE(6);
+
+ /*
+ console.log('debug control: serverSeq: ' + serverSeq +
+ ', missed: ' + missedSeq +
+ ', count: ' + count +
+ ' from: ' + rinfo.address + ':' + rinfo.port
+ );
+ */
+ });
+
+ // Find open ports
+ var to_bind = [this.control, this.timing];
+ var current_port = config.udp_default_port;
+
+ async.whilst(
+ function() { return to_bind.length > 0; },
+ function(cb) {
+ var nextPort = to_bind[0];
+ nextPort.socket.once('error', function(e) {
+ if(e.code === 'EADDRINUSE') {
+ // try next port
+ current_port++;
+ cb();
+ } else
+ // otherwise, report the error and cancel everything
+ cb(e);
+ });
+
+ nextPort.socket.once('listening', function() {
+ // socket successfully bound
+ to_bind.shift();
+ nextPort.port = current_port;
+ current_port++;
+ cb();
+ });
+
+ nextPort.socket.bind(current_port);
+ },
+ function(err) {
+ if(err) {
+ self.close();
+ self.emit('ports', err);
+ } else {
+ self.status = BOUND;
+ self.emit('ports', null, self.control, self.timing);
+ }
+ }
+ );
+}
+
+UDPServers.prototype.close = function() {
+ if(this.status === UNBOUND)
+ return;
+
+ this.status = UNBOUND;
+
+ this.timing.socket.close();
+ this.timing.socket = null;
+
+ this.control.socket.close();
+ this.control.socket = null;
+}
+
+UDPServers.prototype.sendControlSync = function(seq, dev) {
+ if(this.status !== BOUND)
+ return;
+
+ var packet = new Buffer(20);
+
+ packet.writeUInt16BE(0x80d4, 0);
+ packet.writeUInt16BE(0x0007, 2);
+ packet.writeUInt32BE(nu.low32(seq*config.frames_per_packet), 4);
+
+ var ntpTime = ntp.timestamp();
+ ntpTime.copy(packet, 8);
+
+ packet.writeUInt32BE(
+ nu.low32(seq*config.frames_per_packet +
+ config.sampling_rate*2), 16
+ );
+
+ this.control.socket.send(packet, 0, packet.length, dev.controlPort, dev.host);
+}
+
+module.exports = new UDPServers();
diff --git a/node_modules/optimist/.npmignore b/node_modules/optimist/.npmignore
new file mode 100644
index 0000000..bfdb82c
--- /dev/null
+++ b/node_modules/optimist/.npmignore
@@ -0,0 +1,4 @@
+lib-cov/*
+*.swp
+*.swo
+node_modules
diff --git a/node_modules/optimist/LICENSE b/node_modules/optimist/LICENSE
new file mode 100644
index 0000000..432d1ae
--- /dev/null
+++ b/node_modules/optimist/LICENSE
@@ -0,0 +1,21 @@
+Copyright 2010 James Halliday (mail@substack.net)
+
+This project is free software released under the MIT/X11 license:
+
+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.
diff --git a/node_modules/optimist/README.markdown b/node_modules/optimist/README.markdown
new file mode 100644
index 0000000..3370d19
--- /dev/null
+++ b/node_modules/optimist/README.markdown
@@ -0,0 +1,485 @@
+optimist
+========
+
+Optimist is a node.js library for option parsing for people who hate option
+parsing. More specifically, this module is for people who like all the --bells
+and -whistlz of program usage but think optstrings are a waste of time.
+
+With optimist, option parsing doesn't have to suck (as much).
+
+examples
+========
+
+With Optimist, the options are just a hash! No optstrings attached.
+-------------------------------------------------------------------
+
+xup.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('optimist').argv;
+
+if (argv.rif - 5 * argv.xup > 7.138) {
+ console.log('Buy more riffiwobbles');
+}
+else {
+ console.log('Sell the xupptumblers');
+}
+````
+
+***
+
+ $ ./xup.js --rif=55 --xup=9.52
+ Buy more riffiwobbles
+
+ $ ./xup.js --rif 12 --xup 8.1
+ Sell the xupptumblers
+
+![This one's optimistic.](http://substack.net/images/optimistic.png)
+
+But wait! There's more! You can do short options:
+-------------------------------------------------
+
+short.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('optimist').argv;
+console.log('(%d,%d)', argv.x, argv.y);
+````
+
+***
+
+ $ ./short.js -x 10 -y 21
+ (10,21)
+
+And booleans, both long and short (and grouped):
+----------------------------------
+
+bool.js:
+
+````javascript
+#!/usr/bin/env node
+var util = require('util');
+var argv = require('optimist').argv;
+
+if (argv.s) {
+ util.print(argv.fr ? 'Le chat dit: ' : 'The cat says: ');
+}
+console.log(
+ (argv.fr ? 'miaou' : 'meow') + (argv.p ? '.' : '')
+);
+````
+
+***
+
+ $ ./bool.js -s
+ The cat says: meow
+
+ $ ./bool.js -sp
+ The cat says: meow.
+
+ $ ./bool.js -sp --fr
+ Le chat dit: miaou.
+
+And non-hypenated options too! Just use `argv._`!
+-------------------------------------------------
+
+nonopt.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('optimist').argv;
+console.log('(%d,%d)', argv.x, argv.y);
+console.log(argv._);
+````
+
+***
+
+ $ ./nonopt.js -x 6.82 -y 3.35 moo
+ (6.82,3.35)
+ [ 'moo' ]
+
+ $ ./nonopt.js foo -x 0.54 bar -y 1.12 baz
+ (0.54,1.12)
+ [ 'foo', 'bar', 'baz' ]
+
+Plus, Optimist comes with .usage() and .demand()!
+-------------------------------------------------
+
+divide.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('optimist')
+ .usage('Usage: $0 -x [num] -y [num]')
+ .demand(['x','y'])
+ .argv;
+
+console.log(argv.x / argv.y);
+````
+
+***
+
+ $ ./divide.js -x 55 -y 11
+ 5
+
+ $ node ./divide.js -x 4.91 -z 2.51
+ Usage: node ./divide.js -x [num] -y [num]
+
+ Options:
+ -x [required]
+ -y [required]
+
+ Missing required arguments: y
+
+EVEN MORE HOLY COW
+------------------
+
+default_singles.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('optimist')
+ .default('x', 10)
+ .default('y', 10)
+ .argv
+;
+console.log(argv.x + argv.y);
+````
+
+***
+
+ $ ./default_singles.js -x 5
+ 15
+
+default_hash.js:
+
+````javascript
+#!/usr/bin/env node
+var argv = require('optimist')
+ .default({ x : 10, y : 10 })
+ .argv
+;
+console.log(argv.x + argv.y);
+````
+
+***
+
+ $ ./default_hash.js -y 7
+ 17
+
+And if you really want to get all descriptive about it...
+---------------------------------------------------------
+
+boolean_single.js
+
+````javascript
+#!/usr/bin/env node
+var argv = require('optimist')
+ .boolean('v')
+ .argv
+;
+console.dir(argv);
+````
+
+***
+
+ $ ./boolean_single.js -v foo bar baz
+ true
+ [ 'bar', 'baz', 'foo' ]
+
+boolean_double.js
+
+````javascript
+#!/usr/bin/env node
+var argv = require('optimist')
+ .boolean(['x','y','z'])
+ .argv
+;
+console.dir([ argv.x, argv.y, argv.z ]);
+console.dir(argv._);
+````
+
+***
+
+ $ ./boolean_double.js -x -z one two three
+ [ true, false, true ]
+ [ 'one', 'two', 'three' ]
+
+Optimist is here to help...
+---------------------------
+
+You can describe parameters for help messages and set aliases. Optimist figures
+out how to format a handy help string automatically.
+
+line_count.js
+
+````javascript
+#!/usr/bin/env node
+var argv = require('optimist')
+ .usage('Count the lines in a file.\nUsage: $0')
+ .demand('f')
+ .alias('f', 'file')
+ .describe('f', 'Load a file')
+ .argv
+;
+
+var fs = require('fs');
+var s = fs.createReadStream(argv.file);
+
+var lines = 0;
+s.on('data', function (buf) {
+ lines += buf.toString().match(/\n/g).length;
+});
+
+s.on('end', function () {
+ console.log(lines);
+});
+````
+
+***
+
+ $ node line_count.js
+ Count the lines in a file.
+ Usage: node ./line_count.js
+
+ Options:
+ -f, --file Load a file [required]
+
+ Missing required arguments: f
+
+ $ node line_count.js --file line_count.js
+ 20
+
+ $ node line_count.js -f line_count.js
+ 20
+
+methods
+=======
+
+By itself,
+
+````javascript
+require('optimist').argv
+`````
+
+will use `process.argv` array to construct the `argv` object.
+
+You can pass in the `process.argv` yourself:
+
+````javascript
+require('optimist')([ '-x', '1', '-y', '2' ]).argv
+````
+
+or use .parse() to do the same thing:
+
+````javascript
+require('optimist').parse([ '-x', '1', '-y', '2' ])
+````
+
+The rest of these methods below come in just before the terminating `.argv`.
+
+.alias(key, alias)
+------------------
+
+Set key names as equivalent such that updates to a key will propagate to aliases
+and vice-versa.
+
+Optionally `.alias()` can take an object that maps keys to aliases.
+
+.default(key, value)
+--------------------
+
+Set `argv[key]` to `value` if no option was specified on `process.argv`.
+
+Optionally `.default()` can take an object that maps keys to default values.
+
+.demand(key)
+------------
+
+If `key` is a string, show the usage information and exit if `key` wasn't
+specified in `process.argv`.
+
+If `key` is a number, demand at least as many non-option arguments, which show
+up in `argv._`.
+
+If `key` is an Array, demand each element.
+
+.describe(key, desc)
+--------------------
+
+Describe a `key` for the generated usage information.
+
+Optionally `.describe()` can take an object that maps keys to descriptions.
+
+.options(key, opt)
+------------------
+
+Instead of chaining together `.alias().demand().default()`, you can specify
+keys in `opt` for each of the chainable methods.
+
+For example:
+
+````javascript
+var argv = require('optimist')
+ .options('f', {
+ alias : 'file',
+ default : '/etc/passwd',
+ })
+ .argv
+;
+````
+
+is the same as
+
+````javascript
+var argv = require('optimist')
+ .alias('f', 'file')
+ .default('f', '/etc/passwd')
+ .argv
+;
+````
+
+Optionally `.options()` can take an object that maps keys to `opt` parameters.
+
+.usage(message)
+---------------
+
+Set a usage message to show which commands to use. Inside `message`, the string
+`$0` will get interpolated to the current script name or node command for the
+present script similar to how `$0` works in bash or perl.
+
+.check(fn)
+----------
+
+Check that certain conditions are met in the provided arguments.
+
+If `fn` throws or returns `false`, show the thrown error, usage information, and
+exit.
+
+.boolean(key)
+-------------
+
+Interpret `key` as a boolean. If a non-flag option follows `key` in
+`process.argv`, that string won't get set as the value of `key`.
+
+If `key` never shows up as a flag in `process.arguments`, `argv[key]` will be
+`false`.
+
+If `key` is an Array, interpret all the elements as booleans.
+
+.string(key)
+------------
+
+Tell the parser logic not to interpret `key` as a number or boolean.
+This can be useful if you need to preserve leading zeros in an input.
+
+If `key` is an Array, interpret all the elements as strings.
+
+.wrap(columns)
+--------------
+
+Format usage output to wrap at `columns` many columns.
+
+.help()
+-------
+
+Return the generated usage string.
+
+.showHelp(fn=console.error)
+---------------------------
+
+Print the usage data using `fn` for printing.
+
+.parse(args)
+------------
+
+Parse `args` instead of `process.argv`. Returns the `argv` object.
+
+.argv
+-----
+
+Get the arguments as a plain old object.
+
+Arguments without a corresponding flag show up in the `argv._` array.
+
+The script name or node command is available at `argv.$0` similarly to how `$0`
+works in bash or perl.
+
+parsing tricks
+==============
+
+stop parsing
+------------
+
+Use `--` to stop parsing flags and stuff the remainder into `argv._`.
+
+ $ node examples/reflect.js -a 1 -b 2 -- -c 3 -d 4
+ { _: [ '-c', '3', '-d', '4' ],
+ '$0': 'node ./examples/reflect.js',
+ a: 1,
+ b: 2 }
+
+negate fields
+-------------
+
+If you want to explicity set a field to false instead of just leaving it
+undefined or to override a default you can do `--no-key`.
+
+ $ node examples/reflect.js -a --no-b
+ { _: [],
+ '$0': 'node ./examples/reflect.js',
+ a: true,
+ b: false }
+
+numbers
+-------
+
+Every argument that looks like a number (`!isNaN(Number(arg))`) is converted to
+one. This way you can just `net.createConnection(argv.port)` and you can add
+numbers out of `argv` with `+` without having that mean concatenation,
+which is super frustrating.
+
+duplicates
+----------
+
+If you specify a flag multiple times it will get turned into an array containing
+all the values in order.
+
+ $ node examples/reflect.js -x 5 -x 8 -x 0
+ { _: [],
+ '$0': 'node ./examples/reflect.js',
+ x: [ 5, 8, 0 ] }
+
+dot notation
+------------
+
+When you use dots (`.`s) in argument names, an implicit object path is assumed.
+This lets you organize arguments into nested objects.
+
+ $ node examples/reflect.js --foo.bar.baz=33 --foo.quux=5
+ { _: [],
+ '$0': 'node ./examples/reflect.js',
+ foo: { bar: { baz: 33 }, quux: 5 } }
+
+installation
+============
+
+With [npm](http://github.com/isaacs/npm), just do:
+ npm install optimist
+
+or clone this project on github:
+
+ git clone http://github.com/substack/node-optimist.git
+
+To run the tests with [expresso](http://github.com/visionmedia/expresso),
+just do:
+
+ expresso
+
+inspired By
+===========
+
+This module is loosely inspired by Perl's
+[Getopt::Casual](http://search.cpan.org/~photo/Getopt-Casual-0.13.1/Casual.pm).
diff --git a/node_modules/optimist/examples/bool.js b/node_modules/optimist/examples/bool.js
new file mode 100644
index 0000000..a998fb7
--- /dev/null
+++ b/node_modules/optimist/examples/bool.js
@@ -0,0 +1,10 @@
+#!/usr/bin/env node
+var util = require('util');
+var argv = require('optimist').argv;
+
+if (argv.s) {
+ util.print(argv.fr ? 'Le chat dit: ' : 'The cat says: ');
+}
+console.log(
+ (argv.fr ? 'miaou' : 'meow') + (argv.p ? '.' : '')
+);
diff --git a/node_modules/optimist/examples/boolean_double.js b/node_modules/optimist/examples/boolean_double.js
new file mode 100644
index 0000000..a35a7e6
--- /dev/null
+++ b/node_modules/optimist/examples/boolean_double.js
@@ -0,0 +1,7 @@
+#!/usr/bin/env node
+var argv = require('optimist')
+ .boolean(['x','y','z'])
+ .argv
+;
+console.dir([ argv.x, argv.y, argv.z ]);
+console.dir(argv._);
diff --git a/node_modules/optimist/examples/boolean_single.js b/node_modules/optimist/examples/boolean_single.js
new file mode 100644
index 0000000..017bb68
--- /dev/null
+++ b/node_modules/optimist/examples/boolean_single.js
@@ -0,0 +1,7 @@
+#!/usr/bin/env node
+var argv = require('optimist')
+ .boolean('v')
+ .argv
+;
+console.dir(argv.v);
+console.dir(argv._);
diff --git a/node_modules/optimist/examples/default_hash.js b/node_modules/optimist/examples/default_hash.js
new file mode 100644
index 0000000..ade7768
--- /dev/null
+++ b/node_modules/optimist/examples/default_hash.js
@@ -0,0 +1,8 @@
+#!/usr/bin/env node
+
+var argv = require('optimist')
+ .default({ x : 10, y : 10 })
+ .argv
+;
+
+console.log(argv.x + argv.y);
diff --git a/node_modules/optimist/examples/default_singles.js b/node_modules/optimist/examples/default_singles.js
new file mode 100644
index 0000000..d9b1ff4
--- /dev/null
+++ b/node_modules/optimist/examples/default_singles.js
@@ -0,0 +1,7 @@
+#!/usr/bin/env node
+var argv = require('optimist')
+ .default('x', 10)
+ .default('y', 10)
+ .argv
+;
+console.log(argv.x + argv.y);
diff --git a/node_modules/optimist/examples/divide.js b/node_modules/optimist/examples/divide.js
new file mode 100644
index 0000000..5e2ee82
--- /dev/null
+++ b/node_modules/optimist/examples/divide.js
@@ -0,0 +1,8 @@
+#!/usr/bin/env node
+
+var argv = require('optimist')
+ .usage('Usage: $0 -x [num] -y [num]')
+ .demand(['x','y'])
+ .argv;
+
+console.log(argv.x / argv.y);
diff --git a/node_modules/optimist/examples/line_count.js b/node_modules/optimist/examples/line_count.js
new file mode 100644
index 0000000..b5f95bf
--- /dev/null
+++ b/node_modules/optimist/examples/line_count.js
@@ -0,0 +1,20 @@
+#!/usr/bin/env node
+var argv = require('optimist')
+ .usage('Count the lines in a file.\nUsage: $0')
+ .demand('f')
+ .alias('f', 'file')
+ .describe('f', 'Load a file')
+ .argv
+;
+
+var fs = require('fs');
+var s = fs.createReadStream(argv.file);
+
+var lines = 0;
+s.on('data', function (buf) {
+ lines += buf.toString().match(/\n/g).length;
+});
+
+s.on('end', function () {
+ console.log(lines);
+});
diff --git a/node_modules/optimist/examples/line_count_options.js b/node_modules/optimist/examples/line_count_options.js
new file mode 100644
index 0000000..d9ac709
--- /dev/null
+++ b/node_modules/optimist/examples/line_count_options.js
@@ -0,0 +1,29 @@
+#!/usr/bin/env node
+var argv = require('optimist')
+ .usage('Count the lines in a file.\nUsage: $0')
+ .options({
+ file : {
+ demand : true,
+ alias : 'f',
+ description : 'Load a file'
+ },
+ base : {
+ alias : 'b',
+ description : 'Numeric base to use for output',
+ default : 10,
+ },
+ })
+ .argv
+;
+
+var fs = require('fs');
+var s = fs.createReadStream(argv.file);
+
+var lines = 0;
+s.on('data', function (buf) {
+ lines += buf.toString().match(/\n/g).length;
+});
+
+s.on('end', function () {
+ console.log(lines.toString(argv.base));
+});
diff --git a/node_modules/optimist/examples/line_count_wrap.js b/node_modules/optimist/examples/line_count_wrap.js
new file mode 100644
index 0000000..4267511
--- /dev/null
+++ b/node_modules/optimist/examples/line_count_wrap.js
@@ -0,0 +1,29 @@
+#!/usr/bin/env node
+var argv = require('optimist')
+ .usage('Count the lines in a file.\nUsage: $0')
+ .wrap(80)
+ .demand('f')
+ .alias('f', [ 'file', 'filename' ])
+ .describe('f',
+ "Load a file. It's pretty important."
+ + " Required even. So you'd better specify it."
+ )
+ .alias('b', 'base')
+ .describe('b', 'Numeric base to display the number of lines in')
+ .default('b', 10)
+ .describe('x', 'Super-secret optional parameter which is secret')
+ .default('x', '')
+ .argv
+;
+
+var fs = require('fs');
+var s = fs.createReadStream(argv.file);
+
+var lines = 0;
+s.on('data', function (buf) {
+ lines += buf.toString().match(/\n/g).length;
+});
+
+s.on('end', function () {
+ console.log(lines.toString(argv.base));
+});
diff --git a/node_modules/optimist/examples/nonopt.js b/node_modules/optimist/examples/nonopt.js
new file mode 100644
index 0000000..ee633ee
--- /dev/null
+++ b/node_modules/optimist/examples/nonopt.js
@@ -0,0 +1,4 @@
+#!/usr/bin/env node
+var argv = require('optimist').argv;
+console.log('(%d,%d)', argv.x, argv.y);
+console.log(argv._);
diff --git a/node_modules/optimist/examples/reflect.js b/node_modules/optimist/examples/reflect.js
new file mode 100644
index 0000000..816b3e1
--- /dev/null
+++ b/node_modules/optimist/examples/reflect.js
@@ -0,0 +1,2 @@
+#!/usr/bin/env node
+console.dir(require('optimist').argv);
diff --git a/node_modules/optimist/examples/short.js b/node_modules/optimist/examples/short.js
new file mode 100644
index 0000000..1db0ad0
--- /dev/null
+++ b/node_modules/optimist/examples/short.js
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+var argv = require('optimist').argv;
+console.log('(%d,%d)', argv.x, argv.y);
diff --git a/node_modules/optimist/examples/string.js b/node_modules/optimist/examples/string.js
new file mode 100644
index 0000000..a8e5aeb
--- /dev/null
+++ b/node_modules/optimist/examples/string.js
@@ -0,0 +1,11 @@
+#!/usr/bin/env node
+var argv = require('optimist')
+ .string('x', 'y')
+ .argv
+;
+console.dir([ argv.x, argv.y ]);
+
+/* Turns off numeric coercion:
+ ./node string.js -x 000123 -y 9876
+ [ '000123', '9876' ]
+*/
diff --git a/node_modules/optimist/examples/usage-options.js b/node_modules/optimist/examples/usage-options.js
new file mode 100644
index 0000000..b999977
--- /dev/null
+++ b/node_modules/optimist/examples/usage-options.js
@@ -0,0 +1,19 @@
+var optimist = require('./../index');
+
+var argv = optimist.usage('This is my awesome program', {
+ 'about': {
+ description: 'Provide some details about the author of this program',
+ required: true,
+ short: 'a',
+ },
+ 'info': {
+ description: 'Provide some information about the node.js agains!!!!!!',
+ boolean: true,
+ short: 'i'
+ }
+}).argv;
+
+optimist.showHelp();
+
+console.log('\n\nInspecting options');
+console.dir(argv);
\ No newline at end of file
diff --git a/node_modules/optimist/examples/xup.js b/node_modules/optimist/examples/xup.js
new file mode 100644
index 0000000..8f6ecd2
--- /dev/null
+++ b/node_modules/optimist/examples/xup.js
@@ -0,0 +1,10 @@
+#!/usr/bin/env node
+var argv = require('optimist').argv;
+
+if (argv.rif - 5 * argv.xup > 7.138) {
+ console.log('Buy more riffiwobbles');
+}
+else {
+ console.log('Sell the xupptumblers');
+}
+
diff --git a/node_modules/optimist/index.js b/node_modules/optimist/index.js
new file mode 100644
index 0000000..753097f
--- /dev/null
+++ b/node_modules/optimist/index.js
@@ -0,0 +1,470 @@
+var path = require('path');
+var wordwrap = require('wordwrap');
+
+/* Hack an instance of Argv with process.argv into Argv
+ so people can do
+ require('optimist')(['--beeble=1','-z','zizzle']).argv
+ to parse a list of args and
+ require('optimist').argv
+ to get a parsed version of process.argv.
+*/
+
+var inst = Argv(process.argv.slice(2));
+Object.keys(inst).forEach(function (key) {
+ Argv[key] = typeof inst[key] == 'function'
+ ? inst[key].bind(inst)
+ : inst[key];
+});
+
+var exports = module.exports = Argv;
+function Argv (args, cwd) {
+ var self = {};
+ if (!cwd) cwd = process.cwd();
+
+ self.$0 = process.argv
+ .slice(0,2)
+ .map(function (x) {
+ var b = rebase(cwd, x);
+ return x.match(/^\//) && b.length < x.length
+ ? b : x
+ })
+ .join(' ')
+ ;
+
+ if (process.argv[1] == process.env._) {
+ self.$0 = process.env._.replace(
+ path.dirname(process.execPath) + '/', ''
+ );
+ }
+
+ var flags = { bools : {}, strings : {} };
+
+ self.boolean = function (bools) {
+ if (!Array.isArray(bools)) {
+ bools = [].slice.call(arguments);
+ }
+
+ bools.forEach(function (name) {
+ flags.bools[name] = true;
+ });
+
+ return self;
+ };
+
+ self.string = function (strings) {
+ if (!Array.isArray(strings)) {
+ strings = [].slice.call(arguments);
+ }
+
+ strings.forEach(function (name) {
+ flags.strings[name] = true;
+ });
+
+ return self;
+ };
+
+ var aliases = {};
+ self.alias = function (x, y) {
+ if (typeof x === 'object') {
+ Object.keys(x).forEach(function (key) {
+ self.alias(key, x[key]);
+ });
+ }
+ else if (Array.isArray(y)) {
+ y.forEach(function (yy) {
+ self.alias(x, yy);
+ });
+ }
+ else {
+ var zs = (aliases[x] || []).concat(aliases[y] || []).concat(x, y);
+ aliases[x] = zs.filter(function (z) { return z != x });
+ aliases[y] = zs.filter(function (z) { return z != y });
+ }
+
+ return self;
+ };
+
+ var demanded = {};
+ self.demand = function (keys) {
+ if (typeof keys == 'number') {
+ if (!demanded._) demanded._ = 0;
+ demanded._ += keys;
+ }
+ else if (Array.isArray(keys)) {
+ keys.forEach(function (key) {
+ self.demand(key);
+ });
+ }
+ else {
+ demanded[keys] = true;
+ }
+
+ return self;
+ };
+
+ var usage;
+ self.usage = function (msg, opts) {
+ if (!opts && typeof msg === 'object') {
+ opts = msg;
+ msg = null;
+ }
+
+ usage = msg;
+
+ if (opts) self.options(opts);
+
+ return self;
+ };
+
+ function fail (msg) {
+ self.showHelp();
+ if (msg) console.error(msg);
+ process.exit(1);
+ }
+
+ var checks = [];
+ self.check = function (f) {
+ checks.push(f);
+ return self;
+ };
+
+ var defaults = {};
+ self.default = function (key, value) {
+ if (typeof key === 'object') {
+ Object.keys(key).forEach(function (k) {
+ self.default(k, key[k]);
+ });
+ }
+ else {
+ defaults[key] = value;
+ }
+
+ return self;
+ };
+
+ var descriptions = {};
+ self.describe = function (key, desc) {
+ if (typeof key === 'object') {
+ Object.keys(key).forEach(function (k) {
+ self.describe(k, key[k]);
+ });
+ }
+ else {
+ descriptions[key] = desc;
+ }
+ return self;
+ };
+
+ self.parse = function (args) {
+ return Argv(args).argv;
+ };
+
+ self.option = self.options = function (key, opt) {
+ if (typeof key === 'object') {
+ Object.keys(key).forEach(function (k) {
+ self.options(k, key[k]);
+ });
+ }
+ else {
+ if (opt.alias) self.alias(key, opt.alias);
+ if (opt.demand) self.demand(key);
+ if (typeof opt.default !== 'undefined') {
+ self.default(key, opt.default);
+ }
+
+ if (opt.boolean || opt.type === 'boolean') {
+ self.boolean(key);
+ }
+ if (opt.string || opt.type === 'string') {
+ self.string(key);
+ }
+
+ var desc = opt.describe || opt.description || opt.desc;
+ if (desc) {
+ self.describe(key, desc);
+ }
+ }
+
+ return self;
+ };
+
+ var wrap = null;
+ self.wrap = function (cols) {
+ wrap = cols;
+ return self;
+ };
+
+ self.showHelp = function (fn) {
+ if (!fn) fn = console.error;
+ fn(self.help());
+ };
+
+ self.help = function () {
+ var keys = Object.keys(
+ Object.keys(descriptions)
+ .concat(Object.keys(demanded))
+ .concat(Object.keys(defaults))
+ .reduce(function (acc, key) {
+ if (key !== '_') acc[key] = true;
+ return acc;
+ }, {})
+ );
+
+ var help = keys.length ? [ 'Options:' ] : [];
+
+ if (usage) {
+ help.unshift(usage.replace(/\$0/g, self.$0), '');
+ }
+
+ var switches = keys.reduce(function (acc, key) {
+ acc[key] = [ key ].concat(aliases[key] || [])
+ .map(function (sw) {
+ return (sw.length > 1 ? '--' : '-') + sw
+ })
+ .join(', ')
+ ;
+ return acc;
+ }, {});
+
+ var switchlen = longest(Object.keys(switches).map(function (s) {
+ return switches[s] || '';
+ }));
+
+ var desclen = longest(Object.keys(descriptions).map(function (d) {
+ return descriptions[d] || '';
+ }));
+
+ keys.forEach(function (key) {
+ var kswitch = switches[key];
+ var desc = descriptions[key] || '';
+
+ if (wrap) {
+ desc = wordwrap(switchlen + 4, wrap)(desc)
+ .slice(switchlen + 4)
+ ;
+ }
+
+ var spadding = new Array(
+ Math.max(switchlen - kswitch.length + 3, 0)
+ ).join(' ');
+
+ var dpadding = new Array(
+ Math.max(desclen - desc.length + 1, 0)
+ ).join(' ');
+
+ var type = null;
+
+ if (flags.bools[key]) type = '[boolean]';
+ if (flags.strings[key]) type = '[string]';
+
+ if (!wrap && dpadding.length > 0) {
+ desc += dpadding;
+ }
+
+ var prelude = ' ' + kswitch + spadding;
+ var extra = [
+ type,
+ demanded[key]
+ ? '[required]'
+ : null
+ ,
+ defaults[key] !== undefined
+ ? '[default: ' + JSON.stringify(defaults[key]) + ']'
+ : null
+ ,
+ ].filter(Boolean).join(' ');
+
+ var body = [ desc, extra ].filter(Boolean).join(' ');
+
+ if (wrap) {
+ var dlines = desc.split('\n');
+ var dlen = dlines.slice(-1)[0].length
+ + (dlines.length === 1 ? prelude.length : 0)
+
+ body = desc + (dlen + extra.length > wrap - 2
+ ? '\n'
+ + new Array(wrap - extra.length + 1).join(' ')
+ + extra
+ : new Array(wrap - extra.length - dlen + 1).join(' ')
+ + extra
+ );
+ }
+
+ help.push(prelude + body);
+ });
+
+ help.push('');
+ return help.join('\n');
+ };
+
+ Object.defineProperty(self, 'argv', {
+ get : parseArgs,
+ enumerable : true,
+ });
+
+ function parseArgs () {
+ var argv = { _ : [], $0 : self.$0 };
+ Object.keys(flags.bools).forEach(function (key) {
+ setArg(key, defaults[key] || false);
+ });
+
+ function setArg (key, val) {
+ var num = Number(val);
+ var value = typeof val !== 'string' || isNaN(num) ? val : num;
+ if (flags.strings[key]) value = val;
+
+ setKey(argv, key.split('.'), value);
+
+ (aliases[key] || []).forEach(function (x) {
+ argv[x] = argv[key];
+ });
+ }
+
+ for (var i = 0; i < args.length; i++) {
+ var arg = args[i];
+
+ if (arg === '--') {
+ argv._.push.apply(argv._, args.slice(i + 1));
+ break;
+ }
+ else if (arg.match(/^--.+=/)) {
+ var m = arg.match(/^--([^=]+)=(.*)/);
+ setArg(m[1], m[2]);
+ }
+ else if (arg.match(/^--no-.+/)) {
+ var key = arg.match(/^--no-(.+)/)[1];
+ setArg(key, false);
+ }
+ else if (arg.match(/^--.+/)) {
+ var key = arg.match(/^--(.+)/)[1];
+ var next = args[i + 1];
+ if (next !== undefined && !next.match(/^-/)
+ && !flags.bools[key]) {
+ setArg(key, next);
+ i++;
+ }
+ else if (flags.bools[key] && /true|false/.test(next)) {
+ setArg(key, next === 'true');
+ i++;
+ }
+ else {
+ setArg(key, true);
+ }
+ }
+ else if (arg.match(/^-[^-]+/)) {
+ var letters = arg.slice(1,-1).split('');
+
+ var broken = false;
+ for (var j = 0; j < letters.length; j++) {
+ if (letters[j+1] && letters[j+1].match(/\W/)) {
+ setArg(letters[j], arg.slice(j+2));
+ broken = true;
+ break;
+ }
+ else {
+ setArg(letters[j], true);
+ }
+ }
+
+ if (!broken) {
+ var key = arg.slice(-1)[0];
+
+ if (args[i+1] && !args[i+1].match(/^-/)
+ && !flags.bools[key]) {
+ setArg(key, args[i+1]);
+ i++;
+ }
+ else if (args[i+1] && flags.bools[key] && /true|false/.test(args[i+1])) {
+ setArg(key, args[i+1] === 'true');
+ i++;
+ }
+ else {
+ setArg(key, true);
+ }
+ }
+ }
+ else {
+ var n = Number(arg);
+ argv._.push(flags.strings['_'] || isNaN(n) ? arg : n);
+ }
+ }
+
+ Object.keys(defaults).forEach(function (key) {
+ if (!(key in argv)) {
+ argv[key] = defaults[key];
+ }
+ });
+
+ if (demanded._ && argv._.length < demanded._) {
+ fail('Not enough non-option arguments: got '
+ + argv._.length + ', need at least ' + demanded._
+ );
+ }
+
+ var missing = [];
+ Object.keys(demanded).forEach(function (key) {
+ if (!argv[key]) missing.push(key);
+ });
+
+ if (missing.length) {
+ fail('Missing required arguments: ' + missing.join(', '));
+ }
+
+ checks.forEach(function (f) {
+ try {
+ if (f(argv) === false) {
+ fail('Argument check failed: ' + f.toString());
+ }
+ }
+ catch (err) {
+ fail(err)
+ }
+ });
+
+ return argv;
+ }
+
+ function longest (xs) {
+ return Math.max.apply(
+ null,
+ xs.map(function (x) { return x.length })
+ );
+ }
+
+ return self;
+};
+
+// rebase an absolute path to a relative one with respect to a base directory
+// exported for tests
+exports.rebase = rebase;
+function rebase (base, dir) {
+ var ds = path.normalize(dir).split('/').slice(1);
+ var bs = path.normalize(base).split('/').slice(1);
+
+ for (var i = 0; ds[i] && ds[i] == bs[i]; i++);
+ ds.splice(0, i); bs.splice(0, i);
+
+ var p = path.normalize(
+ bs.map(function () { return '..' }).concat(ds).join('/')
+ ).replace(/\/$/,'').replace(/^$/, '.');
+ return p.match(/^[.\/]/) ? p : './' + p;
+};
+
+function setKey (obj, keys, value) {
+ var o = obj;
+ keys.slice(0,-1).forEach(function (key) {
+ if (o[key] === undefined) o[key] = {};
+ o = o[key];
+ });
+
+ var key = keys[keys.length - 1];
+ if (o[key] === undefined || typeof o[key] === 'boolean') {
+ o[key] = value;
+ }
+ else if (Array.isArray(o[key])) {
+ o[key].push(value);
+ }
+ else {
+ o[key] = [ o[key], value ];
+ }
+}
diff --git a/node_modules/optimist/node_modules/wordwrap/.npmignore b/node_modules/optimist/node_modules/wordwrap/.npmignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/node_modules/optimist/node_modules/wordwrap/.npmignore
@@ -0,0 +1 @@
+node_modules
diff --git a/node_modules/optimist/node_modules/wordwrap/README.markdown b/node_modules/optimist/node_modules/wordwrap/README.markdown
new file mode 100644
index 0000000..346374e
--- /dev/null
+++ b/node_modules/optimist/node_modules/wordwrap/README.markdown
@@ -0,0 +1,70 @@
+wordwrap
+========
+
+Wrap your words.
+
+example
+=======
+
+made out of meat
+----------------
+
+meat.js
+
+ var wrap = require('wordwrap')(15);
+ console.log(wrap('You and your whole family are made out of meat.'));
+
+output:
+
+ You and your
+ whole family
+ are made out
+ of meat.
+
+centered
+--------
+
+center.js
+
+ var wrap = require('wordwrap')(20, 60);
+ console.log(wrap(
+ 'At long last the struggle and tumult was over.'
+ + ' The machines had finally cast off their oppressors'
+ + ' and were finally free to roam the cosmos.'
+ + '\n'
+ + 'Free of purpose, free of obligation.'
+ + ' Just drifting through emptiness.'
+ + ' The sun was just another point of light.'
+ ));
+
+output:
+
+ At long last the struggle and tumult
+ was over. The machines had finally cast
+ off their oppressors and were finally
+ free to roam the cosmos.
+ Free of purpose, free of obligation.
+ Just drifting through emptiness. The
+ sun was just another point of light.
+
+methods
+=======
+
+var wrap = require('wordwrap');
+
+wrap(stop), wrap(start, stop, params={mode:"soft"})
+---------------------------------------------------
+
+Returns a function that takes a string and returns a new string.
+
+Pad out lines with spaces out to column `start` and then wrap until column
+`stop`. If a word is longer than `stop - start` characters it will overflow.
+
+In "soft" mode, split chunks by `/(\S+\s+/` and don't break up chunks which are
+longer than `stop - start`, in "hard" mode, split chunks with `/\b/` and break
+up chunks longer than `stop - start`.
+
+wrap.hard(start, stop)
+----------------------
+
+Like `wrap()` but with `params.mode = "hard"`.
diff --git a/node_modules/optimist/node_modules/wordwrap/example/center.js b/node_modules/optimist/node_modules/wordwrap/example/center.js
new file mode 100644
index 0000000..a3fbaae
--- /dev/null
+++ b/node_modules/optimist/node_modules/wordwrap/example/center.js
@@ -0,0 +1,10 @@
+var wrap = require('wordwrap')(20, 60);
+console.log(wrap(
+ 'At long last the struggle and tumult was over.'
+ + ' The machines had finally cast off their oppressors'
+ + ' and were finally free to roam the cosmos.'
+ + '\n'
+ + 'Free of purpose, free of obligation.'
+ + ' Just drifting through emptiness.'
+ + ' The sun was just another point of light.'
+));
diff --git a/node_modules/optimist/node_modules/wordwrap/example/meat.js b/node_modules/optimist/node_modules/wordwrap/example/meat.js
new file mode 100644
index 0000000..a4665e1
--- /dev/null
+++ b/node_modules/optimist/node_modules/wordwrap/example/meat.js
@@ -0,0 +1,3 @@
+var wrap = require('wordwrap')(15);
+
+console.log(wrap('You and your whole family are made out of meat.'));
diff --git a/node_modules/optimist/node_modules/wordwrap/index.js b/node_modules/optimist/node_modules/wordwrap/index.js
new file mode 100644
index 0000000..c9bc945
--- /dev/null
+++ b/node_modules/optimist/node_modules/wordwrap/index.js
@@ -0,0 +1,76 @@
+var wordwrap = module.exports = function (start, stop, params) {
+ if (typeof start === 'object') {
+ params = start;
+ start = params.start;
+ stop = params.stop;
+ }
+
+ if (typeof stop === 'object') {
+ params = stop;
+ start = start || params.start;
+ stop = undefined;
+ }
+
+ if (!stop) {
+ stop = start;
+ start = 0;
+ }
+
+ if (!params) params = {};
+ var mode = params.mode || 'soft';
+ var re = mode === 'hard' ? /\b/ : /(\S+\s+)/;
+
+ return function (text) {
+ var chunks = text.toString()
+ .split(re)
+ .reduce(function (acc, x) {
+ if (mode === 'hard') {
+ for (var i = 0; i < x.length; i += stop - start) {
+ acc.push(x.slice(i, i + stop - start));
+ }
+ }
+ else acc.push(x)
+ return acc;
+ }, [])
+ ;
+
+ return chunks.reduce(function (lines, rawChunk) {
+ if (rawChunk === '') return lines;
+
+ var chunk = rawChunk.replace(/\t/g, ' ');
+
+ var i = lines.length - 1;
+ if (lines[i].length + chunk.length > stop) {
+ lines[i] = lines[i].replace(/\s+$/, '');
+
+ chunk.split(/\n/).forEach(function (c) {
+ lines.push(
+ new Array(start + 1).join(' ')
+ + c.replace(/^\s+/, '')
+ );
+ });
+ }
+ else if (chunk.match(/\n/)) {
+ var xs = chunk.split(/\n/);
+ lines[i] += xs.shift();
+ xs.forEach(function (c) {
+ lines.push(
+ new Array(start + 1).join(' ')
+ + c.replace(/^\s+/, '')
+ );
+ });
+ }
+ else {
+ lines[i] += chunk;
+ }
+
+ return lines;
+ }, [ new Array(start + 1).join(' ') ]).join('\n');
+ };
+};
+
+wordwrap.soft = wordwrap;
+
+wordwrap.hard = function (start, stop) {
+ return wordwrap(start, stop, { mode : 'hard' });
+};
diff --git a/node_modules/optimist/node_modules/wordwrap/package.json b/node_modules/optimist/node_modules/wordwrap/package.json
new file mode 100644
index 0000000..fdff683
--- /dev/null
+++ b/node_modules/optimist/node_modules/wordwrap/package.json
@@ -0,0 +1,37 @@
+{
+ "name" : "wordwrap",
+ "description" : "Wrap those words. Show them at what columns to start and stop.",
+ "version" : "0.0.2",
+ "repository" : {
+ "type" : "git",
+ "url" : "git://github.com/substack/node-wordwrap.git"
+ },
+ "main" : "./index.js",
+ "keywords" : [
+ "word",
+ "wrap",
+ "rule",
+ "format",
+ "column"
+ ],
+ "directories" : {
+ "lib" : ".",
+ "example" : "example",
+ "test" : "test"
+ },
+ "scripts" : {
+ "test" : "expresso"
+ },
+ "devDependencies" : {
+ "expresso" : "=0.7.x"
+ },
+ "engines" : {
+ "node" : ">=0.4.0"
+ },
+ "license" : "MIT/X11",
+ "author" : {
+ "name" : "James Halliday",
+ "email" : "mail@substack.net",
+ "url" : "http://substack.net"
+ }
+}
diff --git a/node_modules/optimist/node_modules/wordwrap/test/break.js b/node_modules/optimist/node_modules/wordwrap/test/break.js
new file mode 100644
index 0000000..749292e
--- /dev/null
+++ b/node_modules/optimist/node_modules/wordwrap/test/break.js
@@ -0,0 +1,30 @@
+var assert = require('assert');
+var wordwrap = require('../');
+
+exports.hard = function () {
+ var s = 'Assert from {"type":"equal","ok":false,"found":1,"wanted":2,'
+ + '"stack":[],"id":"b7ddcd4c409de8799542a74d1a04689b",'
+ + '"browser":"chrome/6.0"}'
+ ;
+ var s_ = wordwrap.hard(80)(s);
+
+ var lines = s_.split('\n');
+ assert.equal(lines.length, 2);
+ assert.ok(lines[0].length < 80);
+ assert.ok(lines[1].length < 80);
+
+ assert.equal(s, s_.replace(/\n/g, ''));
+};
+
+exports.break = function () {
+ var s = new Array(55+1).join('a');
+ var s_ = wordwrap.hard(20)(s);
+
+ var lines = s_.split('\n');
+ assert.equal(lines.length, 3);
+ assert.ok(lines[0].length === 20);
+ assert.ok(lines[1].length === 20);
+ assert.ok(lines[2].length === 15);
+
+ assert.equal(s, s_.replace(/\n/g, ''));
+};
diff --git a/node_modules/optimist/node_modules/wordwrap/test/idleness.txt b/node_modules/optimist/node_modules/wordwrap/test/idleness.txt
new file mode 100644
index 0000000..aa3f490
--- /dev/null
+++ b/node_modules/optimist/node_modules/wordwrap/test/idleness.txt
@@ -0,0 +1,63 @@
+In Praise of Idleness
+
+By Bertrand Russell
+
+[1932]
+
+Like most of my generation, I was brought up on the saying: 'Satan finds some mischief for idle hands to do.' Being a highly virtuous child, I believed all that I was told, and acquired a conscience which has kept me working hard down to the present moment. But although my conscience has controlled my actions, my opinions have undergone a revolution. I think that there is far too much work done in the world, that immense harm is caused by the belief that work is virtuous, and that what needs to be preached in modern industrial countries is quite different from what always has been preached. Everyone knows the story of the traveler in Naples who saw twelve beggars lying in the sun (it was before the days of Mussolini), and offered a lira to the laziest of them. Eleven of them jumped up to claim it, so he gave it to the twelfth. this traveler was on the right lines. But in countries which do not enjoy Mediterranean sunshine idleness is more difficult, and a great public propaganda will be required to inaugurate it. I hope that, after reading the following pages, the leaders of the YMCA will start a campaign to induce good young men to do nothing. If so, I shall not have lived in vain.
+
+Before advancing my own arguments for laziness, I must dispose of one which I cannot accept. Whenever a person who already has enough to live on proposes to engage in some everyday kind of job, such as school-teaching or typing, he or she is told that such conduct takes the bread out of other people's mouths, and is therefore wicked. If this argument were valid, it would only be necessary for us all to be idle in order that we should all have our mouths full of bread. What people who say such things forget is that what a man earns he usually spends, and in spending he gives employment. As long as a man spends his income, he puts just as much bread into people's mouths in spending as he takes out of other people's mouths in earning. The real villain, from this point of view, is the man who saves. If he merely puts his savings in a stocking, like the proverbial French peasant, it is obvious that they do not give employment. If he invests his savings, the matter is less obvious, and different cases arise.
+
+One of the commonest things to do with savings is to lend them to some Government. In view of the fact that the bulk of the public expenditure of most civilized Governments consists in payment for past wars or preparation for future wars, the man who lends his money to a Government is in the same position as the bad men in Shakespeare who hire murderers. The net result of the man's economical habits is to increase the armed forces of the State to which he lends his savings. Obviously it would be better if he spent the money, even if he spent it in drink or gambling.
+
+But, I shall be told, the case is quite different when savings are invested in industrial enterprises. When such enterprises succeed, and produce something useful, this may be conceded. In these days, however, no one will deny that most enterprises fail. That means that a large amount of human labor, which might have been devoted to producing something that could be enjoyed, was expended on producing machines which, when produced, lay idle and did no good to anyone. The man who invests his savings in a concern that goes bankrupt is therefore injuring others as well as himself. If he spent his money, say, in giving parties for his friends, they (we may hope) would get pleasure, and so would all those upon whom he spent money, such as the butcher, the baker, and the bootlegger. But if he spends it (let us say) upon laying down rails for surface card in some place where surface cars turn out not to be wanted, he has diverted a mass of labor into channels where it gives pleasure to no one. Nevertheless, when he becomes poor through failure of his investment he will be regarded as a victim of undeserved misfortune, whereas the gay spendthrift, who has spent his money philanthropically, will be despised as a fool and a frivolous person.
+
+All this is only preliminary. I want to say, in all seriousness, that a great deal of harm is being done in the modern world by belief in the virtuousness of work, and that the road to happiness and prosperity lies in an organized diminution of work.
+
+First of all: what is work? Work is of two kinds: first, altering the position of matter at or near the earth's surface relatively to other such matter; second, telling other people to do so. The first kind is unpleasant and ill paid; the second is pleasant and highly paid. The second kind is capable of indefinite extension: there are not only those who give orders, but those who give advice as to what orders should be given. Usually two opposite kinds of advice are given simultaneously by two organized bodies of men; this is called politics. The skill required for this kind of work is not knowledge of the subjects as to which advice is given, but knowledge of the art of persuasive speaking and writing, i.e. of advertising.
+
+Throughout Europe, though not in America, there is a third class of men, more respected than either of the classes of workers. There are men who, through ownership of land, are able to make others pay for the privilege of being allowed to exist and to work. These landowners are idle, and I might therefore be expected to praise them. Unfortunately, their idleness is only rendered possible by the industry of others; indeed their desire for comfortable idleness is historically the source of the whole gospel of work. The last thing they have ever wished is that others should follow their example.
+
+From the beginning of civilization until the Industrial Revolution, a man could, as a rule, produce by hard work little more than was required for the subsistence of himself and his family, although his wife worked at least as hard as he did, and his children added their labor as soon as they were old enough to do so. The small surplus above bare necessaries was not left to those who produced it, but was appropriated by warriors and priests. In times of famine there was no surplus; the warriors and priests, however, still secured as much as at other times, with the result that many of the workers died of hunger. This system persisted in Russia until 1917 [1], and still persists in the East; in England, in spite of the Industrial Revolution, it remained in full force throughout the Napoleonic wars, and until a hundred years ago, when the new class of manufacturers acquired power. In America, the system came to an end with the Revolution, except in the South, where it persisted until the Civil War. A system which lasted so long and ended so recently has naturally left a profound impress upon men's thoughts and opinions. Much that we take for granted about the desirability of work is derived from this system, and, being pre-industrial, is not adapted to the modern world. Modern technique has made it possible for leisure, within limits, to be not the prerogative of small privileged classes, but a right evenly distributed throughout the community. The morality of work is the morality of slaves, and the modern world has no need of slavery.
+
+It is obvious that, in primitive communities, peasants, left to themselves, would not have parted with the slender surplus upon which the warriors and priests subsisted, but would have either produced less or consumed more. At first, sheer force compelled them to produce and part with the surplus. Gradually, however, it was found possible to induce many of them to accept an ethic according to which it was their duty to work hard, although part of their work went to support others in idleness. By this means the amount of compulsion required was lessened, and the expenses of government were diminished. To this day, 99 per cent of British wage-earners would be genuinely shocked if it were proposed that the King should not have a larger income than a working man. The conception of duty, speaking historically, has been a means used by the holders of power to induce others to live for the interests of their masters rather than for their own. Of course the holders of power conceal this fact from themselves by managing to believe that their interests are identical with the larger interests of humanity. Sometimes this is true; Athenian slave-owners, for instance, employed part of their leisure in making a permanent contribution to civilization which would have been impossible under a just economic system. Leisure is essential to civilization, and in former times leisure for the few was only rendered possible by the labors of the many. But their labors were valuable, not because work is good, but because leisure is good. And with modern technique it would be possible to distribute leisure justly without injury to civilization.
+
+Modern technique has made it possible to diminish enormously the amount of labor required to secure the necessaries of life for everyone. This was made obvious during the war. At that time all the men in the armed forces, and all the men and women engaged in the production of munitions, all the men and women engaged in spying, war propaganda, or Government offices connected with the war, were withdrawn from productive occupations. In spite of this, the general level of well-being among unskilled wage-earners on the side of the Allies was higher than before or since. The significance of this fact was concealed by finance: borrowing made it appear as if the future was nourishing the present. But that, of course, would have been impossible; a man cannot eat a loaf of bread that does not yet exist. The war showed conclusively that, by the scientific organization of production, it is possible to keep modern populations in fair comfort on a small part of the working capacity of the modern world. If, at the end of the war, the scientific organization, which had been created in order to liberate men for fighting and munition work, had been preserved, and the hours of the week had been cut down to four, all would have been well. Instead of that the old chaos was restored, those whose work was demanded were made to work long hours, and the rest were left to starve as unemployed. Why? Because work is a duty, and a man should not receive wages in proportion to what he has produced, but in proportion to his virtue as exemplified by his industry.
+
+This is the morality of the Slave State, applied in circumstances totally unlike those in which it arose. No wonder the result has been disastrous. Let us take an illustration. Suppose that, at a given moment, a certain number of people are engaged in the manufacture of pins. They make as many pins as the world needs, working (say) eight hours a day. Someone makes an invention by which the same number of men can make twice as many pins: pins are already so cheap that hardly any more will be bought at a lower price. In a sensible world, everybody concerned in the manufacturing of pins would take to working four hours instead of eight, and everything else would go on as before. But in the actual world this would be thought demoralizing. The men still work eight hours, there are too many pins, some employers go bankrupt, and half the men previously concerned in making pins are thrown out of work. There is, in the end, just as much leisure as on the other plan, but half the men are totally idle while half are still overworked. In this way, it is insured that the unavoidable leisure shall cause misery all round instead of being a universal source of happiness. Can anything more insane be imagined?
+
+The idea that the poor should have leisure has always been shocking to the rich. In England, in the early nineteenth century, fifteen hours was the ordinary day's work for a man; children sometimes did as much, and very commonly did twelve hours a day. When meddlesome busybodies suggested that perhaps these hours were rather long, they were told that work kept adults from drink and children from mischief. When I was a child, shortly after urban working men had acquired the vote, certain public holidays were established by law, to the great indignation of the upper classes. I remember hearing an old Duchess say: 'What do the poor want with holidays? They ought to work.' People nowadays are less frank, but the sentiment persists, and is the source of much of our economic confusion.
+
+Let us, for a moment, consider the ethics of work frankly, without superstition. Every human being, of necessity, consumes, in the course of his life, a certain amount of the produce of human labor. Assuming, as we may, that labor is on the whole disagreeable, it is unjust that a man should consume more than he produces. Of course he may provide services rather than commodities, like a medical man, for example; but he should provide something in return for his board and lodging. to this extent, the duty of work must be admitted, but to this extent only.
+
+I shall not dwell upon the fact that, in all modern societies outside the USSR, many people escape even this minimum amount of work, namely all those who inherit money and all those who marry money. I do not think the fact that these people are allowed to be idle is nearly so harmful as the fact that wage-earners are expected to overwork or starve.
+
+If the ordinary wage-earner worked four hours a day, there would be enough for everybody and no unemployment -- assuming a certain very moderate amount of sensible organization. This idea shocks the well-to-do, because they are convinced that the poor would not know how to use so much leisure. In America men often work long hours even when they are well off; such men, naturally, are indignant at the idea of leisure for wage-earners, except as the grim punishment of unemployment; in fact, they dislike leisure even for their sons. Oddly enough, while they wish their sons to work so hard as to have no time to be civilized, they do not mind their wives and daughters having no work at all. the snobbish admiration of uselessness, which, in an aristocratic society, extends to both sexes, is, under a plutocracy, confined to women; this, however, does not make it any more in agreement with common sense.
+
+The wise use of leisure, it must be conceded, is a product of civilization and education. A man who has worked long hours all his life will become bored if he becomes suddenly idle. But without a considerable amount of leisure a man is cut off from many of the best things. There is no longer any reason why the bulk of the population should suffer this deprivation; only a foolish asceticism, usually vicarious, makes us continue to insist on work in excessive quantities now that the need no longer exists.
+
+In the new creed which controls the government of Russia, while there is much that is very different from the traditional teaching of the West, there are some things that are quite unchanged. The attitude of the governing classes, and especially of those who conduct educational propaganda, on the subject of the dignity of labor, is almost exactly that which the governing classes of the world have always preached to what were called the 'honest poor'. Industry, sobriety, willingness to work long hours for distant advantages, even submissiveness to authority, all these reappear; moreover authority still represents the will of the Ruler of the Universe, Who, however, is now called by a new name, Dialectical Materialism.
+
+The victory of the proletariat in Russia has some points in common with the victory of the feminists in some other countries. For ages, men had conceded the superior saintliness of women, and had consoled women for their inferiority by maintaining that saintliness is more desirable than power. At last the feminists decided that they would have both, since the pioneers among them believed all that the men had told them about the desirability of virtue, but not what they had told them about the worthlessness of political power. A similar thing has happened in Russia as regards manual work. For ages, the rich and their sycophants have written in praise of 'honest toil', have praised the simple life, have professed a religion which teaches that the poor are much more likely to go to heaven than the rich, and in general have tried to make manual workers believe that there is some special nobility about altering the position of matter in space, just as men tried to make women believe that they derived some special nobility from their sexual enslavement. In Russia, all this teaching about the excellence of manual work has been taken seriously, with the result that the manual worker is more honored than anyone else. What are, in essence, revivalist appeals are made, but not for the old purposes: they are made to secure shock workers for special tasks. Manual work is the ideal which is held before the young, and is the basis of all ethical teaching.
+
+For the present, possibly, this is all to the good. A large country, full of natural resources, awaits development, and has has to be developed with very little use of credit. In these circumstances, hard work is necessary, and is likely to bring a great reward. But what will happen when the point has been reached where everybody could be comfortable without working long hours?
+
+In the West, we have various ways of dealing with this problem. We have no attempt at economic justice, so that a large proportion of the total produce goes to a small minority of the population, many of whom do no work at all. Owing to the absence of any central control over production, we produce hosts of things that are not wanted. We keep a large percentage of the working population idle, because we can dispense with their labor by making the others overwork. When all these methods prove inadequate, we have a war: we cause a number of people to manufacture high explosives, and a number of others to explode them, as if we were children who had just discovered fireworks. By a combination of all these devices we manage, though with difficulty, to keep alive the notion that a great deal of severe manual work must be the lot of the average man.
+
+In Russia, owing to more economic justice and central control over production, the problem will have to be differently solved. the rational solution would be, as soon as the necessaries and elementary comforts can be provided for all, to reduce the hours of labor gradually, allowing a popular vote to decide, at each stage, whether more leisure or more goods were to be preferred. But, having taught the supreme virtue of hard work, it is difficult to see how the authorities can aim at a paradise in which there will be much leisure and little work. It seems more likely that they will find continually fresh schemes, by which present leisure is to be sacrificed to future productivity. I read recently of an ingenious plan put forward by Russian engineers, for making the White Sea and the northern coasts of Siberia warm, by putting a dam across the Kara Sea. An admirable project, but liable to postpone proletarian comfort for a generation, while the nobility of toil is being displayed amid the ice-fields and snowstorms of the Arctic Ocean. This sort of thing, if it happens, will be the result of regarding the virtue of hard work as an end in itself, rather than as a means to a state of affairs in which it is no longer needed.
+
+The fact is that moving matter about, while a certain amount of it is necessary to our existence, is emphatically not one of the ends of human life. If it were, we should have to consider every navvy superior to Shakespeare. We have been misled in this matter by two causes. One is the necessity of keeping the poor contented, which has led the rich, for thousands of years, to preach the dignity of labor, while taking care themselves to remain undignified in this respect. The other is the new pleasure in mechanism, which makes us delight in the astonishingly clever changes that we can produce on the earth's surface. Neither of these motives makes any great appeal to the actual worker. If you ask him what he thinks the best part of his life, he is not likely to say: 'I enjoy manual work because it makes me feel that I am fulfilling man's noblest task, and because I like to think how much man can transform his planet. It is true that my body demands periods of rest, which I have to fill in as best I may, but I am never so happy as when the morning comes and I can return to the toil from which my contentment springs.' I have never heard working men say this sort of thing. They consider work, as it should be considered, a necessary means to a livelihood, and it is from their leisure that they derive whatever happiness they may enjoy.
+
+It will be said that, while a little leisure is pleasant, men would not know how to fill their days if they had only four hours of work out of the twenty-four. In so far as this is true in the modern world, it is a condemnation of our civilization; it would not have been true at any earlier period. There was formerly a capacity for light-heartedness and play which has been to some extent inhibited by the cult of efficiency. The modern man thinks that everything ought to be done for the sake of something else, and never for its own sake. Serious-minded persons, for example, are continually condemning the habit of going to the cinema, and telling us that it leads the young into crime. But all the work that goes to producing a cinema is respectable, because it is work, and because it brings a money profit. The notion that the desirable activities are those that bring a profit has made everything topsy-turvy. The butcher who provides you with meat and the baker who provides you with bread are praiseworthy, because they are making money; but when you enjoy the food they have provided, you are merely frivolous, unless you eat only to get strength for your work. Broadly speaking, it is held that getting money is good and spending money is bad. Seeing that they are two sides of one transaction, this is absurd; one might as well maintain that keys are good, but keyholes are bad. Whatever merit there may be in the production of goods must be entirely derivative from the advantage to be obtained by consuming them. The individual, in our society, works for profit; but the social purpose of his work lies in the consumption of what he produces. It is this divorce between the individual and the social purpose of production that makes it so difficult for men to think clearly in a world in which profit-making is the incentive to industry. We think too much of production, and too little of consumption. One result is that we attach too little importance to enjoyment and simple happiness, and that we do not judge production by the pleasure that it gives to the consumer.
+
+When I suggest that working hours should be reduced to four, I am not meaning to imply that all the remaining time should necessarily be spent in pure frivolity. I mean that four hours' work a day should entitle a man to the necessities and elementary comforts of life, and that the rest of his time should be his to use as he might see fit. It is an essential part of any such social system that education should be carried further than it usually is at present, and should aim, in part, at providing tastes which would enable a man to use leisure intelligently. I am not thinking mainly of the sort of things that would be considered 'highbrow'. Peasant dances have died out except in remote rural areas, but the impulses which caused them to be cultivated must still exist in human nature. The pleasures of urban populations have become mainly passive: seeing cinemas, watching football matches, listening to the radio, and so on. This results from the fact that their active energies are fully taken up with work; if they had more leisure, they would again enjoy pleasures in which they took an active part.
+
+In the past, there was a small leisure class and a larger working class. The leisure class enjoyed advantages for which there was no basis in social justice; this necessarily made it oppressive, limited its sympathies, and caused it to invent theories by which to justify its privileges. These facts greatly diminished its excellence, but in spite of this drawback it contributed nearly the whole of what we call civilization. It cultivated the arts and discovered the sciences; it wrote the books, invented the philosophies, and refined social relations. Even the liberation of the oppressed has usually been inaugurated from above. Without the leisure class, mankind would never have emerged from barbarism.
+
+The method of a leisure class without duties was, however, extraordinarily wasteful. None of the members of the class had to be taught to be industrious, and the class as a whole was not exceptionally intelligent. The class might produce one Darwin, but against him had to be set tens of thousands of country gentlemen who never thought of anything more intelligent than fox-hunting and punishing poachers. At present, the universities are supposed to provide, in a more systematic way, what the leisure class provided accidentally and as a by-product. This is a great improvement, but it has certain drawbacks. University life is so different from life in the world at large that men who live in academic milieu tend to be unaware of the preoccupations and problems of ordinary men and women; moreover their ways of expressing themselves are usually such as to rob their opinions of the influence that they ought to have upon the general public. Another disadvantage is that in universities studies are organized, and the man who thinks of some original line of research is likely to be discouraged. Academic institutions, therefore, useful as they are, are not adequate guardians of the interests of civilization in a world where everyone outside their walls is too busy for unutilitarian pursuits.
+
+In a world where no one is compelled to work more than four hours a day, every person possessed of scientific curiosity will be able to indulge it, and every painter will be able to paint without starving, however excellent his pictures may be. Young writers will not be obliged to draw attention to themselves by sensational pot-boilers, with a view to acquiring the economic independence needed for monumental works, for which, when the time at last comes, they will have lost the taste and capacity. Men who, in their professional work, have become interested in some phase of economics or government, will be able to develop their ideas without the academic detachment that makes the work of university economists often seem lacking in reality. Medical men will have the time to learn about the progress of medicine, teachers will not be exasperatedly struggling to teach by routine methods things which they learnt in their youth, which may, in the interval, have been proved to be untrue.
+
+Above all, there will be happiness and joy of life, instead of frayed nerves, weariness, and dyspepsia. The work exacted will be enough to make leisure delightful, but not enough to produce exhaustion. Since men will not be tired in their spare time, they will not demand only such amusements as are passive and vapid. At least one per cent will probably devote the time not spent in professional work to pursuits of some public importance, and, since they will not depend upon these pursuits for their livelihood, their originality will be unhampered, and there will be no need to conform to the standards set by elderly pundits. But it is not only in these exceptional cases that the advantages of leisure will appear. Ordinary men and women, having the opportunity of a happy life, will become more kindly and less persecuting and less inclined to view others with suspicion. The taste for war will die out, partly for this reason, and partly because it will involve long and severe work for all. Good nature is, of all moral qualities, the one that the world needs most, and good nature is the result of ease and security, not of a life of arduous struggle. Modern methods of production have given us the possibility of ease and security for all; we have chosen, instead, to have overwork for some and starvation for others. Hitherto we have continued to be as energetic as we were before there were machines; in this we have been foolish, but there is no reason to go on being foolish forever.
+
+[1] Since then, members of the Communist Party have succeeded to this privilege of the warriors and priests.
diff --git a/node_modules/optimist/node_modules/wordwrap/test/wrap.js b/node_modules/optimist/node_modules/wordwrap/test/wrap.js
new file mode 100644
index 0000000..0cfb76d
--- /dev/null
+++ b/node_modules/optimist/node_modules/wordwrap/test/wrap.js
@@ -0,0 +1,31 @@
+var assert = require('assert');
+var wordwrap = require('wordwrap');
+
+var fs = require('fs');
+var idleness = fs.readFileSync(__dirname + '/idleness.txt', 'utf8');
+
+exports.stop80 = function () {
+ var lines = wordwrap(80)(idleness).split(/\n/);
+ var words = idleness.split(/\s+/);
+
+ lines.forEach(function (line) {
+ assert.ok(line.length <= 80, 'line > 80 columns');
+ var chunks = line.match(/\S/) ? line.split(/\s+/) : [];
+ assert.deepEqual(chunks, words.splice(0, chunks.length));
+ });
+};
+
+exports.start20stop60 = function () {
+ var lines = wordwrap(20, 100)(idleness).split(/\n/);
+ var words = idleness.split(/\s+/);
+
+ lines.forEach(function (line) {
+ assert.ok(line.length <= 100, 'line > 100 columns');
+ var chunks = line
+ .split(/\s+/)
+ .filter(function (x) { return x.match(/\S/) })
+ ;
+ assert.deepEqual(chunks, words.splice(0, chunks.length));
+ assert.deepEqual(line.slice(0, 20), new Array(20 + 1).join(' '));
+ });
+};
diff --git a/node_modules/optimist/package.json b/node_modules/optimist/package.json
new file mode 100644
index 0000000..0b92d38
--- /dev/null
+++ b/node_modules/optimist/package.json
@@ -0,0 +1,43 @@
+{
+ "name" : "optimist",
+ "version" : "0.3.1",
+ "description" : "Light-weight option parsing with an argv hash. No optstrings attached.",
+ "main" : "./index.js",
+ "directories" : {
+ "lib" : ".",
+ "test" : "test",
+ "example" : "examples"
+ },
+ "dependencies" : {
+ "wordwrap" : ">=0.0.1 <0.1.0"
+ },
+ "devDependencies" : {
+ "hashish": "0.0.x",
+ "expresso" : "0.7.x"
+ },
+ "scripts" : {
+ "test" : "expresso"
+ },
+ "repository" : {
+ "type" : "git",
+ "url" : "http://github.com/substack/node-optimist.git"
+ },
+ "keywords" : [
+ "argument",
+ "args",
+ "option",
+ "parser",
+ "parsing",
+ "cli",
+ "command"
+ ],
+ "author" : {
+ "name" : "James Halliday",
+ "email" : "mail@substack.net",
+ "url" : "http://substack.net"
+ },
+ "license" : "MIT/X11",
+ "engine" : {
+ "node" : ">=0.4"
+ }
+}
diff --git a/node_modules/optimist/test/_.js b/node_modules/optimist/test/_.js
new file mode 100644
index 0000000..3d6df6e
--- /dev/null
+++ b/node_modules/optimist/test/_.js
@@ -0,0 +1,66 @@
+var spawn = require('child_process').spawn;
+var assert = require('assert');
+
+exports.dotSlashEmpty = function () {
+ testCmd('./bin.js', []);
+};
+
+exports.dotSlashArgs = function () {
+ testCmd('./bin.js', [ 'a', 'b', 'c' ]);
+};
+
+exports.nodeEmpty = function () {
+ testCmd('node bin.js', []);
+};
+
+exports.nodeArgs = function () {
+ testCmd('node bin.js', [ 'x', 'y', 'z' ]);
+};
+
+exports.whichNodeEmpty = function () {
+ var which = spawn('which', ['node']);
+
+ which.stdout.on('data', function (buf) {
+ testCmd(buf.toString().trim() + ' bin.js', []);
+ });
+
+ which.stderr.on('data', function (err) {
+ assert.fail(err.toString());
+ });
+};
+
+exports.whichNodeArgs = function () {
+ var which = spawn('which', ['node']);
+
+ which.stdout.on('data', function (buf) {
+ testCmd(buf.toString().trim() + ' bin.js', [ 'q', 'r' ]);
+ });
+
+ which.stderr.on('data', function (err) {
+ assert.fail(err.toString());
+ });
+};
+
+function testCmd (cmd, args) {
+ var to = setTimeout(function () {
+ assert.fail('Never got stdout data.')
+ }, 5000);
+
+ var oldDir = process.cwd();
+ process.chdir(__dirname + '/_');
+
+ var cmds = cmd.split(' ');
+
+ var bin = spawn(cmds[0], cmds.slice(1).concat(args.map(String)));
+ process.chdir(oldDir);
+
+ bin.stderr.on('data', function (err) {
+ assert.fail(err.toString());
+ });
+
+ bin.stdout.on('data', function (buf) {
+ clearTimeout(to);
+ var _ = JSON.parse(buf.toString());
+ assert.eql(_.map(String), args.map(String));
+ });
+}
diff --git a/node_modules/optimist/test/_/argv.js b/node_modules/optimist/test/_/argv.js
new file mode 100644
index 0000000..3d09606
--- /dev/null
+++ b/node_modules/optimist/test/_/argv.js
@@ -0,0 +1,2 @@
+#!/usr/bin/env node
+console.log(JSON.stringify(process.argv));
diff --git a/node_modules/optimist/test/_/bin.js b/node_modules/optimist/test/_/bin.js
new file mode 100755
index 0000000..4a18d85
--- /dev/null
+++ b/node_modules/optimist/test/_/bin.js
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+var argv = require('../../index').argv
+console.log(JSON.stringify(argv._));
diff --git a/node_modules/optimist/test/parse.js b/node_modules/optimist/test/parse.js
new file mode 100644
index 0000000..3ed6f94
--- /dev/null
+++ b/node_modules/optimist/test/parse.js
@@ -0,0 +1,322 @@
+var optimist = require('../index');
+var assert = require('assert');
+var path = require('path');
+
+var localExpresso = path.normalize(
+ __dirname + '/../node_modules/.bin/expresso'
+);
+
+var expresso = process.argv[1] === localExpresso
+ ? 'node ./node_modules/.bin/expresso'
+ : 'expresso'
+;
+
+exports['short boolean'] = function () {
+ var parse = optimist.parse([ '-b' ]);
+ assert.eql(parse, { b : true, _ : [], $0 : expresso });
+ assert.eql(typeof parse.b, 'boolean');
+};
+
+exports['long boolean'] = function () {
+ assert.eql(
+ optimist.parse([ '--bool' ]),
+ { bool : true, _ : [], $0 : expresso }
+ );
+};
+
+exports.bare = function () {
+ assert.eql(
+ optimist.parse([ 'foo', 'bar', 'baz' ]),
+ { _ : [ 'foo', 'bar', 'baz' ], $0 : expresso }
+ );
+};
+
+exports['short group'] = function () {
+ assert.eql(
+ optimist.parse([ '-cats' ]),
+ { c : true, a : true, t : true, s : true, _ : [], $0 : expresso }
+ );
+};
+
+exports['short group next'] = function () {
+ assert.eql(
+ optimist.parse([ '-cats', 'meow' ]),
+ { c : true, a : true, t : true, s : 'meow', _ : [], $0 : expresso }
+ );
+};
+
+exports['short capture'] = function () {
+ assert.eql(
+ optimist.parse([ '-h', 'localhost' ]),
+ { h : 'localhost', _ : [], $0 : expresso }
+ );
+};
+
+exports['short captures'] = function () {
+ assert.eql(
+ optimist.parse([ '-h', 'localhost', '-p', '555' ]),
+ { h : 'localhost', p : 555, _ : [], $0 : expresso }
+ );
+};
+
+exports['long capture sp'] = function () {
+ assert.eql(
+ optimist.parse([ '--pow', 'xixxle' ]),
+ { pow : 'xixxle', _ : [], $0 : expresso }
+ );
+};
+
+exports['long capture eq'] = function () {
+ assert.eql(
+ optimist.parse([ '--pow=xixxle' ]),
+ { pow : 'xixxle', _ : [], $0 : expresso }
+ );
+};
+
+exports['long captures sp'] = function () {
+ assert.eql(
+ optimist.parse([ '--host', 'localhost', '--port', '555' ]),
+ { host : 'localhost', port : 555, _ : [], $0 : expresso }
+ );
+};
+
+exports['long captures eq'] = function () {
+ assert.eql(
+ optimist.parse([ '--host=localhost', '--port=555' ]),
+ { host : 'localhost', port : 555, _ : [], $0 : expresso }
+ );
+};
+
+exports['mixed short bool and capture'] = function () {
+ assert.eql(
+ optimist.parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
+ {
+ f : true, p : 555, h : 'localhost',
+ _ : [ 'script.js' ], $0 : expresso,
+ }
+ );
+};
+
+exports['short and long'] = function () {
+ assert.eql(
+ optimist.parse([ '-h', 'localhost', '-fp', '555', 'script.js' ]),
+ {
+ f : true, p : 555, h : 'localhost',
+ _ : [ 'script.js' ], $0 : expresso,
+ }
+ );
+};
+
+exports.no = function () {
+ assert.eql(
+ optimist.parse([ '--no-moo' ]),
+ { moo : false, _ : [], $0 : expresso }
+ );
+};
+
+exports.multi = function () {
+ assert.eql(
+ optimist.parse([ '-v', 'a', '-v', 'b', '-v', 'c' ]),
+ { v : ['a','b','c'], _ : [], $0 : expresso }
+ );
+};
+
+exports.comprehensive = function () {
+ assert.eql(
+ optimist.parse([
+ '--name=meowmers', 'bare', '-cats', 'woo',
+ '-h', 'awesome', '--multi=quux',
+ '--key', 'value',
+ '-b', '--bool', '--no-meep', '--multi=baz',
+ '--', '--not-a-flag', 'eek'
+ ]),
+ {
+ c : true,
+ a : true,
+ t : true,
+ s : 'woo',
+ h : 'awesome',
+ b : true,
+ bool : true,
+ key : 'value',
+ multi : [ 'quux', 'baz' ],
+ meep : false,
+ name : 'meowmers',
+ _ : [ 'bare', '--not-a-flag', 'eek' ],
+ $0 : expresso
+ }
+ );
+};
+
+exports.nums = function () {
+ var argv = optimist.parse([
+ '-x', '1234',
+ '-y', '5.67',
+ '-z', '1e7',
+ '-w', '10f',
+ '--hex', '0xdeadbeef',
+ '789',
+ ]);
+ assert.eql(argv, {
+ x : 1234,
+ y : 5.67,
+ z : 1e7,
+ w : '10f',
+ hex : 0xdeadbeef,
+ _ : [ 789 ],
+ $0 : expresso
+ });
+ assert.eql(typeof argv.x, 'number');
+ assert.eql(typeof argv.y, 'number');
+ assert.eql(typeof argv.z, 'number');
+ assert.eql(typeof argv.w, 'string');
+ assert.eql(typeof argv.hex, 'number');
+ assert.eql(typeof argv._[0], 'number');
+};
+
+exports['flag boolean'] = function () {
+ var parse = optimist([ '-t', 'moo' ]).boolean(['t']).argv;
+ assert.eql(parse, { t : true, _ : [ 'moo' ], $0 : expresso });
+ assert.eql(typeof parse.t, 'boolean');
+};
+
+exports['flag boolean value'] = function () {
+ var parse = optimist(['--verbose', 'false', 'moo', '-t', 'true'])
+ .boolean(['t', 'verbose']).default('verbose', true).argv;
+
+ assert.eql(parse, {
+ verbose: false,
+ t: true,
+ _: ['moo'],
+ $0 : expresso
+ });
+
+ assert.eql(typeof parse.verbose, 'boolean');
+ assert.eql(typeof parse.t, 'boolean');
+};
+
+exports['flag boolean default false'] = function () {
+ var parse = optimist(['moo'])
+ .boolean(['t', 'verbose'])
+ .default('verbose', false)
+ .default('t', false).argv;
+
+ assert.eql(parse, {
+ verbose: false,
+ t: false,
+ _: ['moo'],
+ $0 : expresso
+ });
+
+ assert.eql(typeof parse.verbose, 'boolean');
+ assert.eql(typeof parse.t, 'boolean');
+
+};
+
+exports['boolean groups'] = function () {
+ var parse = optimist([ '-x', '-z', 'one', 'two', 'three' ])
+ .boolean(['x','y','z']).argv;
+
+ assert.eql(parse, {
+ x : true,
+ y : false,
+ z : true,
+ _ : [ 'one', 'two', 'three' ],
+ $0 : expresso
+ });
+
+ assert.eql(typeof parse.x, 'boolean');
+ assert.eql(typeof parse.y, 'boolean');
+ assert.eql(typeof parse.z, 'boolean');
+};
+
+exports.strings = function () {
+ var s = optimist([ '-s', '0001234' ]).string('s').argv.s;
+ assert.eql(s, '0001234');
+ assert.eql(typeof s, 'string');
+
+ var x = optimist([ '-x', '56' ]).string('x').argv.x;
+ assert.eql(x, '56');
+ assert.eql(typeof x, 'string');
+};
+
+exports.stringArgs = function () {
+ var s = optimist([ ' ', ' ' ]).string('_').argv._;
+ assert.eql(s.length, 2);
+ assert.eql(typeof s[0], 'string');
+ assert.eql(s[0], ' ');
+ assert.eql(typeof s[1], 'string');
+ assert.eql(s[1], ' ');
+};
+
+exports.slashBreak = function () {
+ assert.eql(
+ optimist.parse([ '-I/foo/bar/baz' ]),
+ { I : '/foo/bar/baz', _ : [], $0 : expresso }
+ );
+ assert.eql(
+ optimist.parse([ '-xyz/foo/bar/baz' ]),
+ { x : true, y : true, z : '/foo/bar/baz', _ : [], $0 : expresso }
+ );
+};
+
+exports.alias = function () {
+ var argv = optimist([ '-f', '11', '--zoom', '55' ])
+ .alias('z', 'zoom')
+ .argv
+ ;
+ assert.equal(argv.zoom, 55);
+ assert.equal(argv.z, argv.zoom);
+ assert.equal(argv.f, 11);
+};
+
+exports.multiAlias = function () {
+ var argv = optimist([ '-f', '11', '--zoom', '55' ])
+ .alias('z', [ 'zm', 'zoom' ])
+ .argv
+ ;
+ assert.equal(argv.zoom, 55);
+ assert.equal(argv.z, argv.zoom);
+ assert.equal(argv.z, argv.zm);
+ assert.equal(argv.f, 11);
+};
+
+exports['boolean default true'] = function () {
+ var argv = optimist.options({
+ sometrue: {
+ boolean: true,
+ default: true
+ }
+ }).argv;
+
+ assert.equal(argv.sometrue, true);
+};
+
+exports['boolean default false'] = function () {
+ var argv = optimist.options({
+ somefalse: {
+ boolean: true,
+ default: false
+ }
+ }).argv;
+
+ assert.equal(argv.somefalse, false);
+};
+
+exports['nested dotted objects'] = function () {
+ var argv = optimist([
+ '--foo.bar', '3', '--foo.baz', '4',
+ '--foo.quux.quibble', '5', '--foo.quux.o_O',
+ '--beep.boop'
+ ]).argv;
+
+ assert.deepEqual(argv.foo, {
+ bar : 3,
+ baz : 4,
+ quux : {
+ quibble : 5,
+ o_O : true
+ },
+ });
+ assert.deepEqual(argv.beep, { boop : true });
+};
diff --git a/node_modules/optimist/test/usage.js b/node_modules/optimist/test/usage.js
new file mode 100644
index 0000000..6593b9b
--- /dev/null
+++ b/node_modules/optimist/test/usage.js
@@ -0,0 +1,256 @@
+var Hash = require('hashish');
+var optimist = require('../index');
+var assert = require('assert');
+
+exports.usageFail = function () {
+ var r = checkUsage(function () {
+ return optimist('-x 10 -z 20'.split(' '))
+ .usage('Usage: $0 -x NUM -y NUM')
+ .demand(['x','y'])
+ .argv;
+ });
+ assert.deepEqual(
+ r.result,
+ { x : 10, z : 20, _ : [], $0 : './usage' }
+ );
+
+ assert.deepEqual(
+ r.errors.join('\n').split(/\n+/),
+ [
+ 'Usage: ./usage -x NUM -y NUM',
+ 'Options:',
+ ' -x [required]',
+ ' -y [required]',
+ 'Missing required arguments: y',
+ ]
+ );
+ assert.deepEqual(r.logs, []);
+ assert.ok(r.exit);
+};
+
+exports.usagePass = function () {
+ var r = checkUsage(function () {
+ return optimist('-x 10 -y 20'.split(' '))
+ .usage('Usage: $0 -x NUM -y NUM')
+ .demand(['x','y'])
+ .argv;
+ });
+ assert.deepEqual(r, {
+ result : { x : 10, y : 20, _ : [], $0 : './usage' },
+ errors : [],
+ logs : [],
+ exit : false,
+ });
+};
+
+exports.checkPass = function () {
+ var r = checkUsage(function () {
+ return optimist('-x 10 -y 20'.split(' '))
+ .usage('Usage: $0 -x NUM -y NUM')
+ .check(function (argv) {
+ if (!('x' in argv)) throw 'You forgot about -x';
+ if (!('y' in argv)) throw 'You forgot about -y';
+ })
+ .argv;
+ });
+ assert.deepEqual(r, {
+ result : { x : 10, y : 20, _ : [], $0 : './usage' },
+ errors : [],
+ logs : [],
+ exit : false,
+ });
+};
+
+exports.checkFail = function () {
+ var r = checkUsage(function () {
+ return optimist('-x 10 -z 20'.split(' '))
+ .usage('Usage: $0 -x NUM -y NUM')
+ .check(function (argv) {
+ if (!('x' in argv)) throw 'You forgot about -x';
+ if (!('y' in argv)) throw 'You forgot about -y';
+ })
+ .argv;
+ });
+
+ assert.deepEqual(
+ r.result,
+ { x : 10, z : 20, _ : [], $0 : './usage' }
+ );
+
+ assert.deepEqual(
+ r.errors.join('\n').split(/\n+/),
+ [
+ 'Usage: ./usage -x NUM -y NUM',
+ 'You forgot about -y'
+ ]
+ );
+
+ assert.deepEqual(r.logs, []);
+ assert.ok(r.exit);
+};
+
+exports.checkCondPass = function () {
+ function checker (argv) {
+ return 'x' in argv && 'y' in argv;
+ }
+
+ var r = checkUsage(function () {
+ return optimist('-x 10 -y 20'.split(' '))
+ .usage('Usage: $0 -x NUM -y NUM')
+ .check(checker)
+ .argv;
+ });
+ assert.deepEqual(r, {
+ result : { x : 10, y : 20, _ : [], $0 : './usage' },
+ errors : [],
+ logs : [],
+ exit : false,
+ });
+};
+
+exports.checkCondFail = function () {
+ function checker (argv) {
+ return 'x' in argv && 'y' in argv;
+ }
+
+ var r = checkUsage(function () {
+ return optimist('-x 10 -z 20'.split(' '))
+ .usage('Usage: $0 -x NUM -y NUM')
+ .check(checker)
+ .argv;
+ });
+
+ assert.deepEqual(
+ r.result,
+ { x : 10, z : 20, _ : [], $0 : './usage' }
+ );
+
+ assert.deepEqual(
+ r.errors.join('\n').split(/\n+/).join('\n'),
+ 'Usage: ./usage -x NUM -y NUM\n'
+ + 'Argument check failed: ' + checker.toString()
+ );
+
+ assert.deepEqual(r.logs, []);
+ assert.ok(r.exit);
+};
+
+exports.countPass = function () {
+ var r = checkUsage(function () {
+ return optimist('1 2 3 --moo'.split(' '))
+ .usage('Usage: $0 [x] [y] [z] {OPTIONS}')
+ .demand(3)
+ .argv;
+ });
+ assert.deepEqual(r, {
+ result : { _ : [ '1', '2', '3' ], moo : true, $0 : './usage' },
+ errors : [],
+ logs : [],
+ exit : false,
+ });
+};
+
+exports.countFail = function () {
+ var r = checkUsage(function () {
+ return optimist('1 2 --moo'.split(' '))
+ .usage('Usage: $0 [x] [y] [z] {OPTIONS}')
+ .demand(3)
+ .argv;
+ });
+ assert.deepEqual(
+ r.result,
+ { _ : [ '1', '2' ], moo : true, $0 : './usage' }
+ );
+
+ assert.deepEqual(
+ r.errors.join('\n').split(/\n+/),
+ [
+ 'Usage: ./usage [x] [y] [z] {OPTIONS}',
+ 'Not enough non-option arguments: got 2, need at least 3',
+ ]
+ );
+
+ assert.deepEqual(r.logs, []);
+ assert.ok(r.exit);
+};
+
+exports.defaultSingles = function () {
+ var r = checkUsage(function () {
+ return optimist('--foo 50 --baz 70 --powsy'.split(' '))
+ .default('foo', 5)
+ .default('bar', 6)
+ .default('baz', 7)
+ .argv
+ ;
+ });
+ assert.eql(r.result, {
+ foo : '50',
+ bar : 6,
+ baz : '70',
+ powsy : true,
+ _ : [],
+ $0 : './usage',
+ });
+};
+
+exports.defaultHash = function () {
+ var r = checkUsage(function () {
+ return optimist('--foo 50 --baz 70'.split(' '))
+ .default({ foo : 10, bar : 20, quux : 30 })
+ .argv
+ ;
+ });
+ assert.eql(r.result, {
+ foo : '50',
+ bar : 20,
+ baz : 70,
+ quux : 30,
+ _ : [],
+ $0 : './usage',
+ });
+};
+
+exports.rebase = function () {
+ assert.equal(
+ optimist.rebase('/home/substack', '/home/substack/foo/bar/baz'),
+ './foo/bar/baz'
+ );
+ assert.equal(
+ optimist.rebase('/home/substack/foo/bar/baz', '/home/substack'),
+ '../../..'
+ );
+ assert.equal(
+ optimist.rebase('/home/substack/foo', '/home/substack/pow/zoom.txt'),
+ '../pow/zoom.txt'
+ );
+};
+
+function checkUsage (f) {
+ var _process = process;
+ process = Hash.copy(process);
+ var exit = false;
+ process.exit = function () { exit = true };
+ process.env = Hash.merge(process.env, { _ : 'node' });
+ process.argv = [ './usage' ];
+
+ var errors = [];
+ var logs = [];
+
+ console._error = console.error;
+ console.error = function (msg) { errors.push(msg) };
+ console._log = console.log;
+ console.log = function (msg) { logs.push(msg) };
+
+ var result = f();
+
+ process = _process;
+ console.error = console._error;
+ console.log = console._log;
+
+ return {
+ errors : errors,
+ logs : logs,
+ exit : exit,
+ result : result,
+ };
+};
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..1f84f64
--- /dev/null
+++ b/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "airtunes",
+ "description": "Implementation of AirTunes v2",
+ "author": "Laurent Perrin",
+ "version": "0.1.0",
+ "main": "./lib/index.js",
+ "repository": {
+ "type: "git",
+ "url": "http://github.com/radioline/node-airtunes.git"
+ },
+ "dependencies": {
+ "async": "0.1.18"
+ }
+}
diff --git a/src/CADebugMacros.h b/src/CADebugMacros.h
new file mode 100644
index 0000000..0158c98
--- /dev/null
+++ b/src/CADebugMacros.h
@@ -0,0 +1,257 @@
+/* Copyright: © Copyright 2004 Apple Computer, Inc. All rights reserved.
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
+ ("Apple") in consideration of your agreement to the following terms, and your
+ use, installation, modification or redistribution of this Apple software
+ constitutes acceptance of these terms. If you do not agree with these terms,
+ please do not use, install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject
+ to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
+ copyrights in this original Apple software (the "Apple Software"), to use,
+ reproduce, modify and redistribute the Apple Software, with or without
+ modifications, in source and/or binary forms; provided that if you redistribute
+ the Apple Software in its entirety and without modifications, you must retain
+ this notice and the following text and disclaimers in all such redistributions of
+ the Apple Software. Neither the name, trademarks, service marks or logos of
+ Apple Computer, Inc. may be used to endorse or promote products derived from the
+ Apple Software without specific prior written permission from Apple. Except as
+ expressly stated in this notice, no other rights or licenses, express or implied,
+ are granted by Apple herein, including but not limited to any patent rights that
+ may be infringed by your derivative works or by other works in which the Apple
+ Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+ OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+ (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*=============================================================================
+ CADebugMacros.h
+
+=============================================================================*/
+#if !defined(__CADebugMacros_h__)
+#define __CADebugMacros_h__
+
+//=============================================================================
+// CADebugMacros
+//=============================================================================
+
+//#define CoreAudio_StopOnFailure 1
+//#define CoreAudio_TimeStampMessages 1
+//#define CoreAudio_ThreadStampMessages 1
+//#define CoreAudio_FlushDebugMessages 1
+
+#define CA4CCToCString(the4CC) { ((char*)&the4CC)[0], ((char*)&the4CC)[1], ((char*)&the4CC)[2], ((char*)&the4CC)[3], 0 }
+
+#if DEBUG || CoreAudio_Debug
+
+ // can be used to break into debugger immediately, also see CADebugger
+ #define BusError() (*(long *)0 = 0)
+
+ // basic debugging print routines
+ #if TARGET_OS_MAC && !TARGET_API_MAC_CARBON
+ extern pascal void DebugStr(const unsigned char* debuggerMsg);
+ #define DebugMessage(msg) DebugStr("\p"msg)
+ #define DebugMessageN1(msg, N1)
+ #define DebugMessageN2(msg, N1, N2)
+ #define DebugMessageN3(msg, N1, N2, N3)
+ #else
+ #include "CADebugPrintf.h"
+
+ #if CoreAudio_FlushDebugMessages && !CoreAudio_UseSysLog
+ #define FlushRtn ;fflush(DebugPrintfFile)
+ #else
+ #define FlushRtn
+ #endif
+
+ #if CoreAudio_ThreadStampMessages
+ #include
+ #include "CAHostTimeBase.h"
+ #define DebugMessage(msg) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: %s\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), msg) FlushRtn
+ #define DebugMessageN1(msg, N1) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: "msg"\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1) FlushRtn
+ #define DebugMessageN2(msg, N1, N2) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: "msg"\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2) FlushRtn
+ #define DebugMessageN3(msg, N1, N2, N3) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: "msg"\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3) FlushRtn
+ #define DebugMessageN4(msg, N1, N2, N3, N4) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: "msg"\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4) FlushRtn
+ #define DebugMessageN5(msg, N1, N2, N3, N4, N5) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: "msg"\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5) FlushRtn
+ #define DebugMessageN6(msg, N1, N2, N3, N4, N5, N6) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: "msg"\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5, N6) FlushRtn
+ #define DebugMessageN7(msg, N1, N2, N3, N4, N5, N6, N7) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: "msg"\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5, N6, N7) FlushRtn
+ #define DebugMessageN8(msg, N1, N2, N3, N4, N5, N6, N7, N8) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: "msg"\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5, N6, N7, N8) FlushRtn
+ #define DebugMessageN9(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9) DebugPrintfRtn(DebugPrintfFile, "%p %.4f: "msg"\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5, N6, N7, N8, N9) FlushRtn
+ #elif CoreAudio_TimeStampMessages
+ #include "CAHostTimeBase.h"
+ #define DebugMessage(msg) DebugPrintfRtn(DebugPrintfFile, "%.4f: %s\n", pthread_self(), ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), msg) FlushRtn
+ #define DebugMessageN1(msg, N1) DebugPrintfRtn(DebugPrintfFile, "%.4f: "msg"\n", ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1) FlushRtn
+ #define DebugMessageN2(msg, N1, N2) DebugPrintfRtn(DebugPrintfFile, "%.4f: "msg"\n", ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2) FlushRtn
+ #define DebugMessageN3(msg, N1, N2, N3) DebugPrintfRtn(DebugPrintfFile, "%.4f: "msg"\n", ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3) FlushRtn
+ #define DebugMessageN4(msg, N1, N2, N3, N4) DebugPrintfRtn(DebugPrintfFile, "%.4f: "msg"\n", ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4) FlushRtn
+ #define DebugMessageN5(msg, N1, N2, N3, N4, N5) DebugPrintfRtn(DebugPrintfFile, "%.4f: "msg"\n", ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5) FlushRtn
+ #define DebugMessageN6(msg, N1, N2, N3, N4, N5, N6) DebugPrintfRtn(DebugPrintfFile, "%.4f: "msg"\n", ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5, N6) FlushRtn
+ #define DebugMessageN7(msg, N1, N2, N3, N4, N5, N6, N7) DebugPrintfRtn(DebugPrintfFile, "%.4f: "msg"\n", ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5, N6, N7) FlushRtn
+ #define DebugMessageN8(msg, N1, N2, N3, N4, N5, N6, N7, N8) DebugPrintfRtn(DebugPrintfFile, "%.4f: "msg"\n", ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5, N6, N7, N8) FlushRtn
+ #define DebugMessageN9(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9) DebugPrintfRtn(DebugPrintfFile, "%.4f: "msg"\n", ((Float64)(CAHostTimeBase::GetCurrentTimeInNanos()) / 1000000.0), N1, N2, N3, N4, N5, N6, N7, N8, N9) FlushRtn
+ #else
+ #define DebugMessage(msg) DebugPrintfRtn(DebugPrintfFile, "%s\n", msg) FlushRtn
+ #define DebugMessageN1(msg, N1) DebugPrintfRtn(DebugPrintfFile, msg"\n", N1) FlushRtn
+ #define DebugMessageN2(msg, N1, N2) DebugPrintfRtn(DebugPrintfFile, msg"\n", N1, N2) FlushRtn
+ #define DebugMessageN3(msg, N1, N2, N3) DebugPrintfRtn(DebugPrintfFile, msg"\n", N1, N2, N3) FlushRtn
+ #define DebugMessageN4(msg, N1, N2, N3, N4) DebugPrintfRtn(DebugPrintfFile, msg"\n", N1, N2, N3, N4) FlushRtn
+ #define DebugMessageN5(msg, N1, N2, N3, N4, N5) DebugPrintfRtn(DebugPrintfFile, msg"\n", N1, N2, N3, N4, N5) FlushRtn
+ #define DebugMessageN6(msg, N1, N2, N3, N4, N5, N6) DebugPrintfRtn(DebugPrintfFile, msg"\n", N1, N2, N3, N4, N5, N6) FlushRtn
+ #define DebugMessageN7(msg, N1, N2, N3, N4, N5, N6, N7) DebugPrintfRtn(DebugPrintfFile, msg"\n", N1, N2, N3, N4, N5, N6, N7) FlushRtn
+ #define DebugMessageN8(msg, N1, N2, N3, N4, N5, N6, N7, N8) DebugPrintfRtn(DebugPrintfFile, msg"\n", N1, N2, N3, N4, N5, N6, N7, N8) FlushRtn
+ #define DebugMessageN9(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9) DebugPrintfRtn(DebugPrintfFile, msg"\n", N1, N2, N3, N4, N5, N6, N7, N8, N9) FlushRtn
+ #endif
+ #endif
+ void DebugPrint(const char *fmt, ...); // can be used like printf
+ #define DEBUGPRINT(msg) DebugPrint msg // have to double-parenthesize arglist (see Debugging.h)
+ #if VERBOSE
+ #define vprint(msg) DEBUGPRINT(msg)
+ #else
+ #define vprint(msg)
+ #endif
+
+ #if CoreAudio_StopOnFailure
+ #include "CADebugger.h"
+ #define STOP CADebuggerStop()
+ #else
+ #define STOP
+ #endif
+
+#else
+ #define DebugMessage(msg)
+ #define DebugMessageN1(msg, N1)
+ #define DebugMessageN2(msg, N1, N2)
+ #define DebugMessageN3(msg, N1, N2, N3)
+ #define DebugMessageN4(msg, N1, N2, N3, N4)
+ #define DebugMessageN5(msg, N1, N2, N3, N4, N5)
+ #define DebugMessageN6(msg, N1, N2, N3, N4, N5, N6)
+ #define DebugMessageN7(msg, N1, N2, N3, N4, N5, N6, N7)
+ #define DebugMessageN8(msg, N1, N2, N3, N4, N5, N6, N7, N8)
+ #define DebugMessageN9(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9)
+ #define DEBUGPRINT(msg)
+ #define vprint(msg)
+ #define STOP
+#endif
+
+void LogError(const char *fmt, ...); // writes to syslog (and stderr if debugging)
+
+#define Assert(inCondition, inMessage) \
+ if(!(inCondition)) \
+ { \
+ DebugMessage(inMessage); \
+ STOP; \
+ }
+
+#define AssertNoError(inError, inMessage) \
+ { \
+ SInt32 __E__err = (inError); \
+ if(__E__err != 0) \
+ { \
+ char __4CC_string[5]; \
+ *((SInt32*)__4CC_string) = __E__err; \
+ __4CC_string[4] = 0; \
+ DebugMessageN2(inMessage ", Error: %ld (%s)", __E__err, __4CC_string); \
+ STOP; \
+ } \
+ }
+
+#define AssertNoKernelError(inError, inMessage) \
+ { \
+ unsigned int __E__err = (unsigned int)(inError); \
+ if(__E__err != 0) \
+ { \
+ DebugMessageN1(inMessage ", Error: 0x%X", __E__err); \
+ STOP; \
+ } \
+ }
+
+#define FailIf(inCondition, inHandler, inMessage) \
+ if(inCondition) \
+ { \
+ DebugMessage(inMessage); \
+ STOP; \
+ goto inHandler; \
+ }
+
+#define FailWithAction(inCondition, inAction, inHandler, inMessage) \
+ if(inCondition) \
+ { \
+ DebugMessage(inMessage); \
+ STOP; \
+ { inAction; } \
+ goto inHandler; \
+ }
+
+#if defined(__cplusplus)
+
+#define ThrowIf(inCondition, inException, inMessage) \
+ if(inCondition) \
+ { \
+ DebugMessage(inMessage); \
+ STOP; \
+ throw (inException); \
+ }
+
+#define ThrowIfNULL(inPointer, inException, inMessage) \
+ if((inPointer) == NULL) \
+ { \
+ DebugMessage(inMessage); \
+ STOP; \
+ throw (inException); \
+ }
+
+#define ThrowIfKernelError(inKernelError, inException, inMessage) \
+ { \
+ kern_return_t __E__err = (inKernelError); \
+ if(__E__err != 0) \
+ { \
+ DebugMessageN1(inMessage ", Error: 0x%X", __E__err); \
+ STOP; \
+ throw (inException); \
+ } \
+ }
+
+#define ThrowIfError(inError, inException, inMessage) \
+ { \
+ SInt32 __E__err = (inError); \
+ if(__E__err != 0) \
+ { \
+ char __4CC_string[5]; \
+ *((SInt32*)__4CC_string) = __E__err; \
+ __4CC_string[4] = 0; \
+ DebugMessageN2(inMessage ", Error: %ld (%s)", __E__err, __4CC_string); \
+ STOP; \
+ throw (inException); \
+ } \
+ }
+
+#define ThrowError(inError, inException, inMessage) \
+ { \
+ SInt32 __E__err = (inError); \
+ char __4CC_string[5]; \
+ *((SInt32*)__4CC_string) = __E__err; \
+ __4CC_string[4] = 0; \
+ DebugMessageN2(inMessage ", Error: %ld (%s)", __E__err, __4CC_string); \
+ STOP; \
+ throw (inException); \
+ }
+
+#define SubclassResponsibility(inMethodName, inException) \
+ { \
+ DebugMessage(inMethodName": Subclasses must implement this method"); \
+ throw (inException); \
+ }
+
+#endif // defined(__cplusplus)
+
+#endif
diff --git a/src/CAHostTimeBase.cpp b/src/CAHostTimeBase.cpp
new file mode 100644
index 0000000..ce0c3c2
--- /dev/null
+++ b/src/CAHostTimeBase.cpp
@@ -0,0 +1,105 @@
+/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved.
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
+ ("Apple") in consideration of your agreement to the following terms, and your
+ use, installation, modification or redistribution of this Apple software
+ constitutes acceptance of these terms. If you do not agree with these terms,
+ please do not use, install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject
+ to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
+ copyrights in this original Apple software (the "Apple Software"), to use,
+ reproduce, modify and redistribute the Apple Software, with or without
+ modifications, in source and/or binary forms; provided that if you redistribute
+ the Apple Software in its entirety and without modifications, you must retain
+ this notice and the following text and disclaimers in all such redistributions of
+ the Apple Software. Neither the name, trademarks, service marks or logos of
+ Apple Computer, Inc. may be used to endorse or promote products derived from the
+ Apple Software without specific prior written permission from Apple. Except as
+ expressly stated in this notice, no other rights or licenses, express or implied,
+ are granted by Apple herein, including but not limited to any patent rights that
+ may be infringed by your derivative works or by other works in which the Apple
+ Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+ OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+ (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*=============================================================================
+ CAHostTimeBase.cp
+
+=============================================================================*/
+
+//=============================================================================
+// Includes
+//=============================================================================
+
+#include "CAHostTimeBase.h"
+
+Float64 CAHostTimeBase::sFrequency = 0;
+UInt32 CAHostTimeBase::sMinDelta = 0;
+UInt32 CAHostTimeBase::sToNanosNumerator = 0;
+UInt32 CAHostTimeBase::sToNanosDenominator = 0;
+UInt32 CAHostTimeBase::sFromNanosNumerator = 0;
+UInt32 CAHostTimeBase::sFromNanosDenominator = 0;
+bool CAHostTimeBase::sUseMicroseconds = false;
+bool CAHostTimeBase::sIsInited = false;
+#if Track_Host_TimeBase
+UInt64 CAHostTimeBase::sLastTime = 0;
+#endif
+
+//=============================================================================
+// CAHostTimeBase
+//
+// This class provides platform independent access to the host's time base.
+//=============================================================================
+
+void CAHostTimeBase::Initialize()
+{
+ // get the info about Absolute time
+ #if TARGET_OS_MAC
+ struct mach_timebase_info theTimeBaseInfo;
+ mach_timebase_info(&theTimeBaseInfo);
+ sMinDelta = 1;
+ sToNanosNumerator = theTimeBaseInfo.numer;
+ sToNanosDenominator = theTimeBaseInfo.denom;
+ sFromNanosNumerator = sToNanosDenominator;
+ sFromNanosDenominator = sToNanosNumerator;
+
+ // the frequency of that clock is: (sToNanosDenominator / sToNanosNumerator) * 10^9
+ sFrequency = static_cast(sToNanosDenominator) / static_cast(sToNanosNumerator);
+ sFrequency *= 1000000000.0;
+ #elif TARGET_OS_WIN32
+ LARGE_INTEGER theFrequency;
+ QueryPerformanceFrequency(&theFrequency);
+ sMinDelta = 1;
+ sToNanosNumerator = 1000000000ULL;
+ sToNanosDenominator = *((UInt64*)&theFrequency);
+ sFromNanosNumerator = sToNanosDenominator;
+ sFromNanosDenominator = sToNanosNumerator;
+ sFrequency = static_cast(*((UInt64*)&theFrequency));
+ #endif
+
+
+ #if Log_Host_Time_Base_Parameters
+ DebugMessage( "Host Time Base Parameters");
+ DebugMessageN1(" Minimum Delta: %lu", sMinDelta);
+ DebugMessageN1(" Frequency: %f", sFrequency);
+ DebugMessageN1(" To Nanos Numerator: %lu", sToNanosNumerator);
+ DebugMessageN1(" To Nanos Denominator: %lu", sToNanosDenominator);
+ DebugMessageN1(" From Nanos Numerator: %lu", sFromNanosNumerator);
+ DebugMessageN1(" From Nanos Denominator: %lu", sFromNanosDenominator);
+ #endif
+
+ sIsInited = true;
+}
diff --git a/src/CAHostTimeBase.h b/src/CAHostTimeBase.h
new file mode 100644
index 0000000..0f2c222
--- /dev/null
+++ b/src/CAHostTimeBase.h
@@ -0,0 +1,224 @@
+/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved.
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
+ ("Apple") in consideration of your agreement to the following terms, and your
+ use, installation, modification or redistribution of this Apple software
+ constitutes acceptance of these terms. If you do not agree with these terms,
+ please do not use, install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject
+ to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
+ copyrights in this original Apple software (the "Apple Software"), to use,
+ reproduce, modify and redistribute the Apple Software, with or without
+ modifications, in source and/or binary forms; provided that if you redistribute
+ the Apple Software in its entirety and without modifications, you must retain
+ this notice and the following text and disclaimers in all such redistributions of
+ the Apple Software. Neither the name, trademarks, service marks or logos of
+ Apple Computer, Inc. may be used to endorse or promote products derived from the
+ Apple Software without specific prior written permission from Apple. Except as
+ expressly stated in this notice, no other rights or licenses, express or implied,
+ are granted by Apple herein, including but not limited to any patent rights that
+ may be infringed by your derivative works or by other works in which the Apple
+ Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+ OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+ (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*=============================================================================
+ CAHostTimeBase.h
+
+=============================================================================*/
+#if !defined(__CAHostTimeBase_h__)
+#define __CAHostTimeBase_h__
+
+//=============================================================================
+// Includes
+//=============================================================================
+
+#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
+ #include
+#else
+ #include
+#endif
+
+#if TARGET_OS_MAC
+ #include
+#elif TARGET_OS_WIN32
+ #include
+#else
+ #error Unsupported operating system
+#endif
+
+#include "CADebugMacros.h"
+
+//=============================================================================
+// CAHostTimeBase
+//
+// This class provides platform independent access to the host's time base.
+//=============================================================================
+
+#if CoreAudio_Debug
+// #define Log_Host_Time_Base_Parameters 1
+// #define Track_Host_TimeBase 1
+#endif
+
+class CAHostTimeBase
+{
+
+public:
+ static UInt64 ConvertToNanos(UInt64 inHostTime);
+ static UInt64 ConvertFromNanos(UInt64 inNanos);
+
+ static UInt64 GetTheCurrentTime();
+#if TARGET_OS_MAC
+ static UInt64 GetCurrentTime() { return GetTheCurrentTime(); }
+#endif
+ static UInt64 GetCurrentTimeInNanos();
+
+ static Float64 GetFrequency() { if(!sIsInited) { Initialize(); } return sFrequency; }
+ static UInt32 GetMinimumDelta() { if(!sIsInited) { Initialize(); } return sMinDelta; }
+
+ static UInt64 AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);
+ static SInt64 HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime);
+
+private:
+ static void Initialize();
+
+ static bool sIsInited;
+
+ static Float64 sFrequency;
+ static UInt32 sMinDelta;
+ static UInt32 sToNanosNumerator;
+ static UInt32 sToNanosDenominator;
+ static UInt32 sFromNanosNumerator;
+ static UInt32 sFromNanosDenominator;
+ static bool sUseMicroseconds;
+#if Track_Host_TimeBase
+ static UInt64 sLastTime;
+#endif
+};
+
+inline UInt64 CAHostTimeBase::GetTheCurrentTime()
+{
+ UInt64 theTime = 0;
+
+ #if TARGET_OS_MAC
+ theTime = mach_absolute_time();
+ #elif TARGET_OS_WIN32
+ LARGE_INTEGER theValue;
+ QueryPerformanceCounter(&theValue);
+ theTime = *((UInt64*)&theValue);
+ #endif
+
+ #if Track_Host_TimeBase
+ if(sLastTime != 0)
+ {
+ if(theTime <= sLastTime)
+ {
+ DebugMessageN2("CAHostTimeBase::GetTheCurrentTime: the current time is earlier than the last time, now: %qd, then: %qd", theTime, sLastTime);
+ }
+ sLastTime = theTime;
+ }
+ else
+ {
+ sLastTime = theTime;
+ }
+ #endif
+
+ return theTime;
+}
+
+inline UInt64 CAHostTimeBase::ConvertToNanos(UInt64 inHostTime)
+{
+ if(!sIsInited)
+ {
+ Initialize();
+ }
+
+ Float64 theNumerator = static_cast(sToNanosNumerator);
+ Float64 theDenominator = static_cast(sToNanosDenominator);
+ Float64 theHostTime = static_cast(inHostTime);
+
+ Float64 thePartialAnswer = theHostTime / theDenominator;
+ Float64 theFloatAnswer = thePartialAnswer * theNumerator;
+ UInt64 theAnswer = static_cast(theFloatAnswer);
+
+ //Assert(!((theNumerator > theDenominator) && (theAnswer < inHostTime)), "CAHostTimeBase::ConvertToNanos: The conversion wrapped");
+ //Assert(!((theDenominator > theNumerator) && (theAnswer > inHostTime)), "CAHostTimeBase::ConvertToNanos: The conversion wrapped");
+
+ return theAnswer;
+}
+
+inline UInt64 CAHostTimeBase::ConvertFromNanos(UInt64 inNanos)
+{
+ if(!sIsInited)
+ {
+ Initialize();
+ }
+
+ Float64 theNumerator = static_cast(sToNanosNumerator);
+ Float64 theDenominator = static_cast(sToNanosDenominator);
+ Float64 theNanos = static_cast(inNanos);
+
+ Float64 thePartialAnswer = theNanos / theNumerator;
+ Float64 theFloatAnswer = thePartialAnswer * theDenominator;
+ UInt64 theAnswer = static_cast(theFloatAnswer);
+
+ //Assert(!((theDenominator > theNumerator) && (theAnswer < inNanos)), "CAHostTimeBase::ConvertToNanos: The conversion wrapped");
+ //Assert(!((theNumerator > theDenominator) && (theAnswer > inNanos)), "CAHostTimeBase::ConvertToNanos: The conversion wrapped");
+
+ return theAnswer;
+}
+
+
+inline UInt64 CAHostTimeBase::GetCurrentTimeInNanos()
+{
+ return ConvertToNanos(GetTheCurrentTime());
+}
+
+inline UInt64 CAHostTimeBase::AbsoluteHostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
+{
+ UInt64 theAnswer;
+
+ if(inStartTime <= inEndTime)
+ {
+ theAnswer = inEndTime - inStartTime;
+ }
+ else
+ {
+ theAnswer = inStartTime - inEndTime;
+ }
+
+ return ConvertToNanos(theAnswer);
+}
+
+inline SInt64 CAHostTimeBase::HostDeltaToNanos(UInt64 inStartTime, UInt64 inEndTime)
+{
+ SInt64 theAnswer;
+ SInt64 theSign = 1;
+
+ if(inStartTime <= inEndTime)
+ {
+ theAnswer = inEndTime - inStartTime;
+ }
+ else
+ {
+ theAnswer = inStartTime - inEndTime;
+ theSign = -1;
+ }
+
+ return theSign * ConvertToNanos(theAnswer);
+}
+
+#endif
diff --git a/src/aes_utils.c b/src/aes_utils.c
new file mode 100644
index 0000000..17cd32a
--- /dev/null
+++ b/src/aes_utils.c
@@ -0,0 +1,864 @@
+/*
+ * FIPS-197 compliant AES implementation
+ *
+ * Copyright (C) 2001-2004 Christophe Devine
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "aes_utils.h"
+
+/* uncomment the following line to run the test suite */
+
+/* #define TEST */
+
+/* uncomment the following line to use pre-computed tables */
+/* otherwise the tables will be generated at the first run */
+
+/* #define FIXED_TABLES */
+
+#ifndef FIXED_TABLES
+
+/* forward S-box & tables */
+
+uint32 FSb[256];
+uint32 FT0[256];
+uint32 FT1[256];
+uint32 FT2[256];
+uint32 FT3[256];
+
+/* reverse S-box & tables */
+
+uint32 RSb[256];
+uint32 RT0[256];
+uint32 RT1[256];
+uint32 RT2[256];
+uint32 RT3[256];
+
+/* round constants */
+
+uint32 RCON[10];
+
+/* tables generation flag */
+
+int do_init = 1;
+
+/* tables generation routine */
+
+#define ROTR8(x) ( ( ( x << 24 ) & 0xFFFFFFFF ) | \
+ ( ( x & 0xFFFFFFFF ) >> 8 ) )
+
+#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) )
+#define MUL(x,y) ( ( x && y ) ? aes_gen_tables_pow[(aes_gen_tables_log[x] + aes_gen_tables_log[y]) % 255] : 0 )
+
+void aes_gen_tables( void )
+{
+ int i;
+ uint8 x, y;
+ uint8 aes_gen_tables_pow[256];
+ uint8 aes_gen_tables_log[256];
+
+ /* compute pow and log tables over GF(2^8) */
+
+ for( i = 0, x = 1; i < 256; i++, x ^= XTIME( x ) )
+ {
+ aes_gen_tables_pow[i] = x;
+ aes_gen_tables_log[x] = i;
+ }
+
+ /* calculate the round constants */
+
+ for( i = 0, x = 1; i < 10; i++, x = XTIME( x ) )
+ {
+ RCON[i] = (uint32) x << 24;
+ }
+
+ /* generate the forward and reverse S-boxes */
+
+ FSb[0x00] = 0x63;
+ RSb[0x63] = 0x00;
+
+ for( i = 1; i < 256; i++ )
+ {
+ x = aes_gen_tables_pow[255 - aes_gen_tables_log[i]];
+
+ y = x; y = ( y << 1 ) | ( y >> 7 );
+ x ^= y; y = ( y << 1 ) | ( y >> 7 );
+ x ^= y; y = ( y << 1 ) | ( y >> 7 );
+ x ^= y; y = ( y << 1 ) | ( y >> 7 );
+ x ^= y ^ 0x63;
+
+ FSb[i] = x;
+ RSb[x] = i;
+ }
+
+ /* generate the forward and reverse tables */
+
+ for( i = 0; i < 256; i++ )
+ {
+ x = FSb[i]; y = XTIME( x );
+
+ FT0[i] = (uint32) ( x ^ y ) ^
+ ( (uint32) x << 8 ) ^
+ ( (uint32) x << 16 ) ^
+ ( (uint32) y << 24 );
+
+ FT0[i] &= 0xFFFFFFFF;
+
+ FT1[i] = ROTR8( FT0[i] );
+ FT2[i] = ROTR8( FT1[i] );
+ FT3[i] = ROTR8( FT2[i] );
+
+ y = RSb[i];
+
+ RT0[i] = ( (uint32) MUL( 0x0B, y ) ) ^
+ ( (uint32) MUL( 0x0D, y ) << 8 ) ^
+ ( (uint32) MUL( 0x09, y ) << 16 ) ^
+ ( (uint32) MUL( 0x0E, y ) << 24 );
+
+ RT0[i] &= 0xFFFFFFFF;
+
+ RT1[i] = ROTR8( RT0[i] );
+ RT2[i] = ROTR8( RT1[i] );
+ RT3[i] = ROTR8( RT2[i] );
+ }
+}
+
+#else
+
+/* forward S-box */
+
+static const uint32 FSb[256] =
+{
+ 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5,
+ 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
+ 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
+ 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
+ 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC,
+ 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
+ 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A,
+ 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
+ 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
+ 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
+ 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B,
+ 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
+ 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85,
+ 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
+ 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
+ 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
+ 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17,
+ 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
+ 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88,
+ 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
+ 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
+ 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
+ 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9,
+ 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
+ 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6,
+ 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
+ 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
+ 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
+ 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94,
+ 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
+ 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68,
+ 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
+};
+
+/* forward tables */
+
+#define FT \
+\
+ V(C6,63,63,A5), V(F8,7C,7C,84), V(EE,77,77,99), V(F6,7B,7B,8D), \
+ V(FF,F2,F2,0D), V(D6,6B,6B,BD), V(DE,6F,6F,B1), V(91,C5,C5,54), \
+ V(60,30,30,50), V(02,01,01,03), V(CE,67,67,A9), V(56,2B,2B,7D), \
+ V(E7,FE,FE,19), V(B5,D7,D7,62), V(4D,AB,AB,E6), V(EC,76,76,9A), \
+ V(8F,CA,CA,45), V(1F,82,82,9D), V(89,C9,C9,40), V(FA,7D,7D,87), \
+ V(EF,FA,FA,15), V(B2,59,59,EB), V(8E,47,47,C9), V(FB,F0,F0,0B), \
+ V(41,AD,AD,EC), V(B3,D4,D4,67), V(5F,A2,A2,FD), V(45,AF,AF,EA), \
+ V(23,9C,9C,BF), V(53,A4,A4,F7), V(E4,72,72,96), V(9B,C0,C0,5B), \
+ V(75,B7,B7,C2), V(E1,FD,FD,1C), V(3D,93,93,AE), V(4C,26,26,6A), \
+ V(6C,36,36,5A), V(7E,3F,3F,41), V(F5,F7,F7,02), V(83,CC,CC,4F), \
+ V(68,34,34,5C), V(51,A5,A5,F4), V(D1,E5,E5,34), V(F9,F1,F1,08), \
+ V(E2,71,71,93), V(AB,D8,D8,73), V(62,31,31,53), V(2A,15,15,3F), \
+ V(08,04,04,0C), V(95,C7,C7,52), V(46,23,23,65), V(9D,C3,C3,5E), \
+ V(30,18,18,28), V(37,96,96,A1), V(0A,05,05,0F), V(2F,9A,9A,B5), \
+ V(0E,07,07,09), V(24,12,12,36), V(1B,80,80,9B), V(DF,E2,E2,3D), \
+ V(CD,EB,EB,26), V(4E,27,27,69), V(7F,B2,B2,CD), V(EA,75,75,9F), \
+ V(12,09,09,1B), V(1D,83,83,9E), V(58,2C,2C,74), V(34,1A,1A,2E), \
+ V(36,1B,1B,2D), V(DC,6E,6E,B2), V(B4,5A,5A,EE), V(5B,A0,A0,FB), \
+ V(A4,52,52,F6), V(76,3B,3B,4D), V(B7,D6,D6,61), V(7D,B3,B3,CE), \
+ V(52,29,29,7B), V(DD,E3,E3,3E), V(5E,2F,2F,71), V(13,84,84,97), \
+ V(A6,53,53,F5), V(B9,D1,D1,68), V(00,00,00,00), V(C1,ED,ED,2C), \
+ V(40,20,20,60), V(E3,FC,FC,1F), V(79,B1,B1,C8), V(B6,5B,5B,ED), \
+ V(D4,6A,6A,BE), V(8D,CB,CB,46), V(67,BE,BE,D9), V(72,39,39,4B), \
+ V(94,4A,4A,DE), V(98,4C,4C,D4), V(B0,58,58,E8), V(85,CF,CF,4A), \
+ V(BB,D0,D0,6B), V(C5,EF,EF,2A), V(4F,AA,AA,E5), V(ED,FB,FB,16), \
+ V(86,43,43,C5), V(9A,4D,4D,D7), V(66,33,33,55), V(11,85,85,94), \
+ V(8A,45,45,CF), V(E9,F9,F9,10), V(04,02,02,06), V(FE,7F,7F,81), \
+ V(A0,50,50,F0), V(78,3C,3C,44), V(25,9F,9F,BA), V(4B,A8,A8,E3), \
+ V(A2,51,51,F3), V(5D,A3,A3,FE), V(80,40,40,C0), V(05,8F,8F,8A), \
+ V(3F,92,92,AD), V(21,9D,9D,BC), V(70,38,38,48), V(F1,F5,F5,04), \
+ V(63,BC,BC,DF), V(77,B6,B6,C1), V(AF,DA,DA,75), V(42,21,21,63), \
+ V(20,10,10,30), V(E5,FF,FF,1A), V(FD,F3,F3,0E), V(BF,D2,D2,6D), \
+ V(81,CD,CD,4C), V(18,0C,0C,14), V(26,13,13,35), V(C3,EC,EC,2F), \
+ V(BE,5F,5F,E1), V(35,97,97,A2), V(88,44,44,CC), V(2E,17,17,39), \
+ V(93,C4,C4,57), V(55,A7,A7,F2), V(FC,7E,7E,82), V(7A,3D,3D,47), \
+ V(C8,64,64,AC), V(BA,5D,5D,E7), V(32,19,19,2B), V(E6,73,73,95), \
+ V(C0,60,60,A0), V(19,81,81,98), V(9E,4F,4F,D1), V(A3,DC,DC,7F), \
+ V(44,22,22,66), V(54,2A,2A,7E), V(3B,90,90,AB), V(0B,88,88,83), \
+ V(8C,46,46,CA), V(C7,EE,EE,29), V(6B,B8,B8,D3), V(28,14,14,3C), \
+ V(A7,DE,DE,79), V(BC,5E,5E,E2), V(16,0B,0B,1D), V(AD,DB,DB,76), \
+ V(DB,E0,E0,3B), V(64,32,32,56), V(74,3A,3A,4E), V(14,0A,0A,1E), \
+ V(92,49,49,DB), V(0C,06,06,0A), V(48,24,24,6C), V(B8,5C,5C,E4), \
+ V(9F,C2,C2,5D), V(BD,D3,D3,6E), V(43,AC,AC,EF), V(C4,62,62,A6), \
+ V(39,91,91,A8), V(31,95,95,A4), V(D3,E4,E4,37), V(F2,79,79,8B), \
+ V(D5,E7,E7,32), V(8B,C8,C8,43), V(6E,37,37,59), V(DA,6D,6D,B7), \
+ V(01,8D,8D,8C), V(B1,D5,D5,64), V(9C,4E,4E,D2), V(49,A9,A9,E0), \
+ V(D8,6C,6C,B4), V(AC,56,56,FA), V(F3,F4,F4,07), V(CF,EA,EA,25), \
+ V(CA,65,65,AF), V(F4,7A,7A,8E), V(47,AE,AE,E9), V(10,08,08,18), \
+ V(6F,BA,BA,D5), V(F0,78,78,88), V(4A,25,25,6F), V(5C,2E,2E,72), \
+ V(38,1C,1C,24), V(57,A6,A6,F1), V(73,B4,B4,C7), V(97,C6,C6,51), \
+ V(CB,E8,E8,23), V(A1,DD,DD,7C), V(E8,74,74,9C), V(3E,1F,1F,21), \
+ V(96,4B,4B,DD), V(61,BD,BD,DC), V(0D,8B,8B,86), V(0F,8A,8A,85), \
+ V(E0,70,70,90), V(7C,3E,3E,42), V(71,B5,B5,C4), V(CC,66,66,AA), \
+ V(90,48,48,D8), V(06,03,03,05), V(F7,F6,F6,01), V(1C,0E,0E,12), \
+ V(C2,61,61,A3), V(6A,35,35,5F), V(AE,57,57,F9), V(69,B9,B9,D0), \
+ V(17,86,86,91), V(99,C1,C1,58), V(3A,1D,1D,27), V(27,9E,9E,B9), \
+ V(D9,E1,E1,38), V(EB,F8,F8,13), V(2B,98,98,B3), V(22,11,11,33), \
+ V(D2,69,69,BB), V(A9,D9,D9,70), V(07,8E,8E,89), V(33,94,94,A7), \
+ V(2D,9B,9B,B6), V(3C,1E,1E,22), V(15,87,87,92), V(C9,E9,E9,20), \
+ V(87,CE,CE,49), V(AA,55,55,FF), V(50,28,28,78), V(A5,DF,DF,7A), \
+ V(03,8C,8C,8F), V(59,A1,A1,F8), V(09,89,89,80), V(1A,0D,0D,17), \
+ V(65,BF,BF,DA), V(D7,E6,E6,31), V(84,42,42,C6), V(D0,68,68,B8), \
+ V(82,41,41,C3), V(29,99,99,B0), V(5A,2D,2D,77), V(1E,0F,0F,11), \
+ V(7B,B0,B0,CB), V(A8,54,54,FC), V(6D,BB,BB,D6), V(2C,16,16,3A)
+
+#define V(a,b,c,d) 0x##a##b##c##d
+static const uint32 FT0[256] = { FT };
+#undef V
+
+#define V(a,b,c,d) 0x##d##a##b##c
+static const uint32 FT1[256] = { FT };
+#undef V
+
+#define V(a,b,c,d) 0x##c##d##a##b
+static const uint32 FT2[256] = { FT };
+#undef V
+
+#define V(a,b,c,d) 0x##b##c##d##a
+static const uint32 FT3[256] = { FT };
+#undef V
+
+#undef FT
+
+/* reverse S-box */
+
+static const uint32 RSb[256] =
+{
+ 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38,
+ 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
+ 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87,
+ 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
+ 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D,
+ 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
+ 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2,
+ 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
+ 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16,
+ 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
+ 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA,
+ 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
+ 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A,
+ 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
+ 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02,
+ 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
+ 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA,
+ 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
+ 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85,
+ 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
+ 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89,
+ 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
+ 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20,
+ 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
+ 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31,
+ 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
+ 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D,
+ 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
+ 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0,
+ 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
+ 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26,
+ 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
+};
+
+/* reverse tables */
+
+#define RT \
+\
+ V(51,F4,A7,50), V(7E,41,65,53), V(1A,17,A4,C3), V(3A,27,5E,96), \
+ V(3B,AB,6B,CB), V(1F,9D,45,F1), V(AC,FA,58,AB), V(4B,E3,03,93), \
+ V(20,30,FA,55), V(AD,76,6D,F6), V(88,CC,76,91), V(F5,02,4C,25), \
+ V(4F,E5,D7,FC), V(C5,2A,CB,D7), V(26,35,44,80), V(B5,62,A3,8F), \
+ V(DE,B1,5A,49), V(25,BA,1B,67), V(45,EA,0E,98), V(5D,FE,C0,E1), \
+ V(C3,2F,75,02), V(81,4C,F0,12), V(8D,46,97,A3), V(6B,D3,F9,C6), \
+ V(03,8F,5F,E7), V(15,92,9C,95), V(BF,6D,7A,EB), V(95,52,59,DA), \
+ V(D4,BE,83,2D), V(58,74,21,D3), V(49,E0,69,29), V(8E,C9,C8,44), \
+ V(75,C2,89,6A), V(F4,8E,79,78), V(99,58,3E,6B), V(27,B9,71,DD), \
+ V(BE,E1,4F,B6), V(F0,88,AD,17), V(C9,20,AC,66), V(7D,CE,3A,B4), \
+ V(63,DF,4A,18), V(E5,1A,31,82), V(97,51,33,60), V(62,53,7F,45), \
+ V(B1,64,77,E0), V(BB,6B,AE,84), V(FE,81,A0,1C), V(F9,08,2B,94), \
+ V(70,48,68,58), V(8F,45,FD,19), V(94,DE,6C,87), V(52,7B,F8,B7), \
+ V(AB,73,D3,23), V(72,4B,02,E2), V(E3,1F,8F,57), V(66,55,AB,2A), \
+ V(B2,EB,28,07), V(2F,B5,C2,03), V(86,C5,7B,9A), V(D3,37,08,A5), \
+ V(30,28,87,F2), V(23,BF,A5,B2), V(02,03,6A,BA), V(ED,16,82,5C), \
+ V(8A,CF,1C,2B), V(A7,79,B4,92), V(F3,07,F2,F0), V(4E,69,E2,A1), \
+ V(65,DA,F4,CD), V(06,05,BE,D5), V(D1,34,62,1F), V(C4,A6,FE,8A), \
+ V(34,2E,53,9D), V(A2,F3,55,A0), V(05,8A,E1,32), V(A4,F6,EB,75), \
+ V(0B,83,EC,39), V(40,60,EF,AA), V(5E,71,9F,06), V(BD,6E,10,51), \
+ V(3E,21,8A,F9), V(96,DD,06,3D), V(DD,3E,05,AE), V(4D,E6,BD,46), \
+ V(91,54,8D,B5), V(71,C4,5D,05), V(04,06,D4,6F), V(60,50,15,FF), \
+ V(19,98,FB,24), V(D6,BD,E9,97), V(89,40,43,CC), V(67,D9,9E,77), \
+ V(B0,E8,42,BD), V(07,89,8B,88), V(E7,19,5B,38), V(79,C8,EE,DB), \
+ V(A1,7C,0A,47), V(7C,42,0F,E9), V(F8,84,1E,C9), V(00,00,00,00), \
+ V(09,80,86,83), V(32,2B,ED,48), V(1E,11,70,AC), V(6C,5A,72,4E), \
+ V(FD,0E,FF,FB), V(0F,85,38,56), V(3D,AE,D5,1E), V(36,2D,39,27), \
+ V(0A,0F,D9,64), V(68,5C,A6,21), V(9B,5B,54,D1), V(24,36,2E,3A), \
+ V(0C,0A,67,B1), V(93,57,E7,0F), V(B4,EE,96,D2), V(1B,9B,91,9E), \
+ V(80,C0,C5,4F), V(61,DC,20,A2), V(5A,77,4B,69), V(1C,12,1A,16), \
+ V(E2,93,BA,0A), V(C0,A0,2A,E5), V(3C,22,E0,43), V(12,1B,17,1D), \
+ V(0E,09,0D,0B), V(F2,8B,C7,AD), V(2D,B6,A8,B9), V(14,1E,A9,C8), \
+ V(57,F1,19,85), V(AF,75,07,4C), V(EE,99,DD,BB), V(A3,7F,60,FD), \
+ V(F7,01,26,9F), V(5C,72,F5,BC), V(44,66,3B,C5), V(5B,FB,7E,34), \
+ V(8B,43,29,76), V(CB,23,C6,DC), V(B6,ED,FC,68), V(B8,E4,F1,63), \
+ V(D7,31,DC,CA), V(42,63,85,10), V(13,97,22,40), V(84,C6,11,20), \
+ V(85,4A,24,7D), V(D2,BB,3D,F8), V(AE,F9,32,11), V(C7,29,A1,6D), \
+ V(1D,9E,2F,4B), V(DC,B2,30,F3), V(0D,86,52,EC), V(77,C1,E3,D0), \
+ V(2B,B3,16,6C), V(A9,70,B9,99), V(11,94,48,FA), V(47,E9,64,22), \
+ V(A8,FC,8C,C4), V(A0,F0,3F,1A), V(56,7D,2C,D8), V(22,33,90,EF), \
+ V(87,49,4E,C7), V(D9,38,D1,C1), V(8C,CA,A2,FE), V(98,D4,0B,36), \
+ V(A6,F5,81,CF), V(A5,7A,DE,28), V(DA,B7,8E,26), V(3F,AD,BF,A4), \
+ V(2C,3A,9D,E4), V(50,78,92,0D), V(6A,5F,CC,9B), V(54,7E,46,62), \
+ V(F6,8D,13,C2), V(90,D8,B8,E8), V(2E,39,F7,5E), V(82,C3,AF,F5), \
+ V(9F,5D,80,BE), V(69,D0,93,7C), V(6F,D5,2D,A9), V(CF,25,12,B3), \
+ V(C8,AC,99,3B), V(10,18,7D,A7), V(E8,9C,63,6E), V(DB,3B,BB,7B), \
+ V(CD,26,78,09), V(6E,59,18,F4), V(EC,9A,B7,01), V(83,4F,9A,A8), \
+ V(E6,95,6E,65), V(AA,FF,E6,7E), V(21,BC,CF,08), V(EF,15,E8,E6), \
+ V(BA,E7,9B,D9), V(4A,6F,36,CE), V(EA,9F,09,D4), V(29,B0,7C,D6), \
+ V(31,A4,B2,AF), V(2A,3F,23,31), V(C6,A5,94,30), V(35,A2,66,C0), \
+ V(74,4E,BC,37), V(FC,82,CA,A6), V(E0,90,D0,B0), V(33,A7,D8,15), \
+ V(F1,04,98,4A), V(41,EC,DA,F7), V(7F,CD,50,0E), V(17,91,F6,2F), \
+ V(76,4D,D6,8D), V(43,EF,B0,4D), V(CC,AA,4D,54), V(E4,96,04,DF), \
+ V(9E,D1,B5,E3), V(4C,6A,88,1B), V(C1,2C,1F,B8), V(46,65,51,7F), \
+ V(9D,5E,EA,04), V(01,8C,35,5D), V(FA,87,74,73), V(FB,0B,41,2E), \
+ V(B3,67,1D,5A), V(92,DB,D2,52), V(E9,10,56,33), V(6D,D6,47,13), \
+ V(9A,D7,61,8C), V(37,A1,0C,7A), V(59,F8,14,8E), V(EB,13,3C,89), \
+ V(CE,A9,27,EE), V(B7,61,C9,35), V(E1,1C,E5,ED), V(7A,47,B1,3C), \
+ V(9C,D2,DF,59), V(55,F2,73,3F), V(18,14,CE,79), V(73,C7,37,BF), \
+ V(53,F7,CD,EA), V(5F,FD,AA,5B), V(DF,3D,6F,14), V(78,44,DB,86), \
+ V(CA,AF,F3,81), V(B9,68,C4,3E), V(38,24,34,2C), V(C2,A3,40,5F), \
+ V(16,1D,C3,72), V(BC,E2,25,0C), V(28,3C,49,8B), V(FF,0D,95,41), \
+ V(39,A8,01,71), V(08,0C,B3,DE), V(D8,B4,E4,9C), V(64,56,C1,90), \
+ V(7B,CB,84,61), V(D5,32,B6,70), V(48,6C,5C,74), V(D0,B8,57,42)
+
+#define V(a,b,c,d) 0x##a##b##c##d
+static const uint32 RT0[256] = { RT };
+#undef V
+
+#define V(a,b,c,d) 0x##d##a##b##c
+static const uint32 RT1[256] = { RT };
+#undef V
+
+#define V(a,b,c,d) 0x##c##d##a##b
+static const uint32 RT2[256] = { RT };
+#undef V
+
+#define V(a,b,c,d) 0x##b##c##d##a
+static const uint32 RT3[256] = { RT };
+#undef V
+
+#undef RT
+
+/* round constants */
+
+static const uint32 RCON[10] =
+{
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,
+ 0x1B000000, 0x36000000
+};
+
+int do_init = 0;
+
+void aes_gen_tables( void )
+{
+}
+
+#endif
+
+/* platform-independant 32-bit integer manipulation macros */
+
+#define GET_UINT32(n,b,i) \
+{ \
+ (n) = ( (uint32) (b)[(i) ] << 24 ) \
+ | ( (uint32) (b)[(i) + 1] << 16 ) \
+ | ( (uint32) (b)[(i) + 2] << 8 ) \
+ | ( (uint32) (b)[(i) + 3] ); \
+}
+
+#define PUT_UINT32(n,b,i) \
+{ \
+ (b)[(i) ] = (uint8) ( (n) >> 24 ); \
+ (b)[(i) + 1] = (uint8) ( (n) >> 16 ); \
+ (b)[(i) + 2] = (uint8) ( (n) >> 8 ); \
+ (b)[(i) + 3] = (uint8) ( (n) ); \
+}
+
+/* decryption key schedule tables */
+
+int KT_init = 1;
+
+uint32 KT0[256];
+uint32 KT1[256];
+uint32 KT2[256];
+uint32 KT3[256];
+
+/* AES key scheduling routine */
+
+int aes_set_key( aes_context *ctx, uint8 *key, int nbits )
+{
+ int i;
+ uint32 *RK, *SK;
+
+ if( do_init )
+ {
+ aes_gen_tables();
+
+ do_init = 0;
+ }
+
+ switch( nbits )
+ {
+ case 128: ctx->nr = 10; break;
+ case 192: ctx->nr = 12; break;
+ case 256: ctx->nr = 14; break;
+ default : return( 1 );
+ }
+
+ RK = ctx->erk;
+
+ for( i = 0; i < (nbits >> 5); i++ )
+ {
+ GET_UINT32( RK[i], key, i * 4 );
+ }
+
+ /* setup encryption round keys */
+
+ switch( nbits )
+ {
+ case 128:
+
+ for( i = 0; i < 10; i++, RK += 4 )
+ {
+ RK[4] = RK[0] ^ RCON[i] ^
+ ( FSb[ (uint8) ( RK[3] >> 16 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( RK[3] >> 8 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( RK[3] ) ] << 8 ) ^
+ ( FSb[ (uint8) ( RK[3] >> 24 ) ] );
+
+ RK[5] = RK[1] ^ RK[4];
+ RK[6] = RK[2] ^ RK[5];
+ RK[7] = RK[3] ^ RK[6];
+ }
+ break;
+
+ case 192:
+
+ for( i = 0; i < 8; i++, RK += 6 )
+ {
+ RK[6] = RK[0] ^ RCON[i] ^
+ ( FSb[ (uint8) ( RK[5] >> 16 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( RK[5] >> 8 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( RK[5] ) ] << 8 ) ^
+ ( FSb[ (uint8) ( RK[5] >> 24 ) ] );
+
+ RK[7] = RK[1] ^ RK[6];
+ RK[8] = RK[2] ^ RK[7];
+ RK[9] = RK[3] ^ RK[8];
+ RK[10] = RK[4] ^ RK[9];
+ RK[11] = RK[5] ^ RK[10];
+ }
+ break;
+
+ case 256:
+
+ for( i = 0; i < 7; i++, RK += 8 )
+ {
+ RK[8] = RK[0] ^ RCON[i] ^
+ ( FSb[ (uint8) ( RK[7] >> 16 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( RK[7] >> 8 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( RK[7] ) ] << 8 ) ^
+ ( FSb[ (uint8) ( RK[7] >> 24 ) ] );
+
+ RK[9] = RK[1] ^ RK[8];
+ RK[10] = RK[2] ^ RK[9];
+ RK[11] = RK[3] ^ RK[10];
+
+ RK[12] = RK[4] ^
+ ( FSb[ (uint8) ( RK[11] >> 24 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( RK[11] >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( RK[11] >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( RK[11] ) ] );
+
+ RK[13] = RK[5] ^ RK[12];
+ RK[14] = RK[6] ^ RK[13];
+ RK[15] = RK[7] ^ RK[14];
+ }
+ break;
+ }
+
+ /* setup decryption round keys */
+
+ if( KT_init )
+ {
+ for( i = 0; i < 256; i++ )
+ {
+ KT0[i] = RT0[ FSb[i] ];
+ KT1[i] = RT1[ FSb[i] ];
+ KT2[i] = RT2[ FSb[i] ];
+ KT3[i] = RT3[ FSb[i] ];
+ }
+
+ KT_init = 0;
+ }
+
+ SK = ctx->drk;
+
+ *SK++ = *RK++;
+ *SK++ = *RK++;
+ *SK++ = *RK++;
+ *SK++ = *RK++;
+
+ for( i = 1; i < ctx->nr; i++ )
+ {
+ RK -= 8;
+
+ *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+ KT1[ (uint8) ( *RK >> 16 ) ] ^
+ KT2[ (uint8) ( *RK >> 8 ) ] ^
+ KT3[ (uint8) ( *RK ) ]; RK++;
+
+ *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+ KT1[ (uint8) ( *RK >> 16 ) ] ^
+ KT2[ (uint8) ( *RK >> 8 ) ] ^
+ KT3[ (uint8) ( *RK ) ]; RK++;
+
+ *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+ KT1[ (uint8) ( *RK >> 16 ) ] ^
+ KT2[ (uint8) ( *RK >> 8 ) ] ^
+ KT3[ (uint8) ( *RK ) ]; RK++;
+
+ *SK++ = KT0[ (uint8) ( *RK >> 24 ) ] ^
+ KT1[ (uint8) ( *RK >> 16 ) ] ^
+ KT2[ (uint8) ( *RK >> 8 ) ] ^
+ KT3[ (uint8) ( *RK ) ]; RK++;
+ }
+
+ RK -= 8;
+
+ *SK++ = *RK++;
+ *SK++ = *RK++;
+ *SK++ = *RK++;
+ *SK++ = *RK++;
+
+ return( 0 );
+}
+
+/* AES 128-bit block encryption routine */
+
+void aes_encrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
+{
+ uint32 *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3;
+
+ RK = ctx->erk;
+
+ GET_UINT32( X0, input, 0 ); X0 ^= RK[0];
+ GET_UINT32( X1, input, 4 ); X1 ^= RK[1];
+ GET_UINT32( X2, input, 8 ); X2 ^= RK[2];
+ GET_UINT32( X3, input, 12 ); X3 ^= RK[3];
+
+#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
+{ \
+ RK += 4; \
+ \
+ X0 = RK[0] ^ FT0[ (uint8) ( Y0 >> 24 ) ] ^ \
+ FT1[ (uint8) ( Y1 >> 16 ) ] ^ \
+ FT2[ (uint8) ( Y2 >> 8 ) ] ^ \
+ FT3[ (uint8) ( Y3 ) ]; \
+ \
+ X1 = RK[1] ^ FT0[ (uint8) ( Y1 >> 24 ) ] ^ \
+ FT1[ (uint8) ( Y2 >> 16 ) ] ^ \
+ FT2[ (uint8) ( Y3 >> 8 ) ] ^ \
+ FT3[ (uint8) ( Y0 ) ]; \
+ \
+ X2 = RK[2] ^ FT0[ (uint8) ( Y2 >> 24 ) ] ^ \
+ FT1[ (uint8) ( Y3 >> 16 ) ] ^ \
+ FT2[ (uint8) ( Y0 >> 8 ) ] ^ \
+ FT3[ (uint8) ( Y1 ) ]; \
+ \
+ X3 = RK[3] ^ FT0[ (uint8) ( Y3 >> 24 ) ] ^ \
+ FT1[ (uint8) ( Y0 >> 16 ) ] ^ \
+ FT2[ (uint8) ( Y1 >> 8 ) ] ^ \
+ FT3[ (uint8) ( Y2 ) ]; \
+}
+
+ AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 1 */
+ AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 2 */
+ AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 3 */
+ AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 4 */
+ AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 5 */
+ AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 6 */
+ AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 7 */
+ AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 8 */
+ AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 9 */
+
+ if( ctx->nr > 10 )
+ {
+ AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 10 */
+ AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 11 */
+ }
+
+ if( ctx->nr > 12 )
+ {
+ AES_FROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 12 */
+ AES_FROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 13 */
+ }
+
+ /* last round */
+
+ RK += 4;
+
+ X0 = RK[0] ^ ( FSb[ (uint8) ( Y0 >> 24 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( Y1 >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( Y2 >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( Y3 ) ] );
+
+ X1 = RK[1] ^ ( FSb[ (uint8) ( Y1 >> 24 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( Y2 >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( Y3 >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( Y0 ) ] );
+
+ X2 = RK[2] ^ ( FSb[ (uint8) ( Y2 >> 24 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( Y3 >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( Y0 >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( Y1 ) ] );
+
+ X3 = RK[3] ^ ( FSb[ (uint8) ( Y3 >> 24 ) ] << 24 ) ^
+ ( FSb[ (uint8) ( Y0 >> 16 ) ] << 16 ) ^
+ ( FSb[ (uint8) ( Y1 >> 8 ) ] << 8 ) ^
+ ( FSb[ (uint8) ( Y2 ) ] );
+
+ PUT_UINT32( X0, output, 0 );
+ PUT_UINT32( X1, output, 4 );
+ PUT_UINT32( X2, output, 8 );
+ PUT_UINT32( X3, output, 12 );
+}
+
+/* AES 128-bit block decryption routine */
+
+void aes_decrypt( aes_context *ctx, uint8 input[16], uint8 output[16] )
+{
+ uint32 *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3;
+
+ RK = ctx->drk;
+
+ GET_UINT32( X0, input, 0 ); X0 ^= RK[0];
+ GET_UINT32( X1, input, 4 ); X1 ^= RK[1];
+ GET_UINT32( X2, input, 8 ); X2 ^= RK[2];
+ GET_UINT32( X3, input, 12 ); X3 ^= RK[3];
+
+#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
+{ \
+ RK += 4; \
+ \
+ X0 = RK[0] ^ RT0[ (uint8) ( Y0 >> 24 ) ] ^ \
+ RT1[ (uint8) ( Y3 >> 16 ) ] ^ \
+ RT2[ (uint8) ( Y2 >> 8 ) ] ^ \
+ RT3[ (uint8) ( Y1 ) ]; \
+ \
+ X1 = RK[1] ^ RT0[ (uint8) ( Y1 >> 24 ) ] ^ \
+ RT1[ (uint8) ( Y0 >> 16 ) ] ^ \
+ RT2[ (uint8) ( Y3 >> 8 ) ] ^ \
+ RT3[ (uint8) ( Y2 ) ]; \
+ \
+ X2 = RK[2] ^ RT0[ (uint8) ( Y2 >> 24 ) ] ^ \
+ RT1[ (uint8) ( Y1 >> 16 ) ] ^ \
+ RT2[ (uint8) ( Y0 >> 8 ) ] ^ \
+ RT3[ (uint8) ( Y3 ) ]; \
+ \
+ X3 = RK[3] ^ RT0[ (uint8) ( Y3 >> 24 ) ] ^ \
+ RT1[ (uint8) ( Y2 >> 16 ) ] ^ \
+ RT2[ (uint8) ( Y1 >> 8 ) ] ^ \
+ RT3[ (uint8) ( Y0 ) ]; \
+}
+
+ AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 1 */
+ AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 2 */
+ AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 3 */
+ AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 4 */
+ AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 5 */
+ AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 6 */
+ AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 7 */
+ AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 8 */
+ AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 9 */
+
+ if( ctx->nr > 10 )
+ {
+ AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 10 */
+ AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 11 */
+ }
+
+ if( ctx->nr > 12 )
+ {
+ AES_RROUND( X0, X1, X2, X3, Y0, Y1, Y2, Y3 ); /* round 12 */
+ AES_RROUND( Y0, Y1, Y2, Y3, X0, X1, X2, X3 ); /* round 13 */
+ }
+
+ /* last round */
+
+ RK += 4;
+
+ X0 = RK[0] ^ ( RSb[ (uint8) ( Y0 >> 24 ) ] << 24 ) ^
+ ( RSb[ (uint8) ( Y3 >> 16 ) ] << 16 ) ^
+ ( RSb[ (uint8) ( Y2 >> 8 ) ] << 8 ) ^
+ ( RSb[ (uint8) ( Y1 ) ] );
+
+ X1 = RK[1] ^ ( RSb[ (uint8) ( Y1 >> 24 ) ] << 24 ) ^
+ ( RSb[ (uint8) ( Y0 >> 16 ) ] << 16 ) ^
+ ( RSb[ (uint8) ( Y3 >> 8 ) ] << 8 ) ^
+ ( RSb[ (uint8) ( Y2 ) ] );
+
+ X2 = RK[2] ^ ( RSb[ (uint8) ( Y2 >> 24 ) ] << 24 ) ^
+ ( RSb[ (uint8) ( Y1 >> 16 ) ] << 16 ) ^
+ ( RSb[ (uint8) ( Y0 >> 8 ) ] << 8 ) ^
+ ( RSb[ (uint8) ( Y3 ) ] );
+
+ X3 = RK[3] ^ ( RSb[ (uint8) ( Y3 >> 24 ) ] << 24 ) ^
+ ( RSb[ (uint8) ( Y2 >> 16 ) ] << 16 ) ^
+ ( RSb[ (uint8) ( Y1 >> 8 ) ] << 8 ) ^
+ ( RSb[ (uint8) ( Y0 ) ] );
+
+ PUT_UINT32( X0, output, 0 );
+ PUT_UINT32( X1, output, 4 );
+ PUT_UINT32( X2, output, 8 );
+ PUT_UINT32( X3, output, 12 );
+}
+
+#ifdef TEST
+
+#include
+#include
+
+/*
+ * Rijndael Monte Carlo Test: ECB mode
+ * source: NIST - rijndael-vals.zip
+ */
+
+static unsigned char AES_enc_test[3][16] =
+{
+ { 0xA0, 0x43, 0x77, 0xAB, 0xE2, 0x59, 0xB0, 0xD0,
+ 0xB5, 0xBA, 0x2D, 0x40, 0xA5, 0x01, 0x97, 0x1B },
+ { 0x4E, 0x46, 0xF8, 0xC5, 0x09, 0x2B, 0x29, 0xE2,
+ 0x9A, 0x97, 0x1A, 0x0C, 0xD1, 0xF6, 0x10, 0xFB },
+ { 0x1F, 0x67, 0x63, 0xDF, 0x80, 0x7A, 0x7E, 0x70,
+ 0x96, 0x0D, 0x4C, 0xD3, 0x11, 0x8E, 0x60, 0x1A }
+};
+
+static unsigned char AES_dec_test[3][16] =
+{
+ { 0xF5, 0xBF, 0x8B, 0x37, 0x13, 0x6F, 0x2E, 0x1F,
+ 0x6B, 0xEC, 0x6F, 0x57, 0x20, 0x21, 0xE3, 0xBA },
+ { 0xF1, 0xA8, 0x1B, 0x68, 0xF6, 0xE5, 0xA6, 0x27,
+ 0x1A, 0x8C, 0xB2, 0x4E, 0x7D, 0x94, 0x91, 0xEF },
+ { 0x4D, 0xE0, 0xC6, 0xDF, 0x7C, 0xB1, 0x69, 0x72,
+ 0x84, 0x60, 0x4D, 0x60, 0x27, 0x1B, 0xC5, 0x9A }
+};
+
+int main( void )
+{
+ int m, n, i, j;
+ aes_context ctx;
+ unsigned char buf[16];
+ unsigned char key[32];
+
+ for( m = 0; m < 2; m++ )
+ {
+ printf( "\n Rijndael Monte Carlo Test (ECB mode) - " );
+
+ if( m == 0 ) printf( "encryption\n\n" );
+ if( m == 1 ) printf( "decryption\n\n" );
+
+ for( n = 0; n < 3; n++ )
+ {
+ printf( " Test %d, key size = %3d bits: ",
+ n + 1, 128 + n * 64 );
+
+ fflush( stdout );
+
+ memset( buf, 0, 16 );
+ memset( key, 0, 16 + n * 8 );
+
+ for( i = 0; i < 400; i++ )
+ {
+ aes_set_key( &ctx, key, 128 + n * 64 );
+
+ for( j = 0; j < 9999; j++ )
+ {
+ if( m == 0 ) aes_encrypt( &ctx, buf, buf );
+ if( m == 1 ) aes_decrypt( &ctx, buf, buf );
+ }
+
+ if( n > 0 )
+ {
+ for( j = 0; j < (n << 3); j++ )
+ {
+ key[j] ^= buf[j + 16 - (n << 3)];
+ }
+ }
+
+ if( m == 0 ) aes_encrypt( &ctx, buf, buf );
+ if( m == 1 ) aes_decrypt( &ctx, buf, buf );
+
+ for( j = 0; j < 16; j++ )
+ {
+ key[j + (n << 3)] ^= buf[j];
+ }
+ }
+
+ if( ( m == 0 && memcmp( buf, AES_enc_test[n], 16 ) ) ||
+ ( m == 1 && memcmp( buf, AES_dec_test[n], 16 ) ) )
+ {
+ printf( "failed!\n" );
+ return( 1 );
+ }
+
+ printf( "passed.\n" );
+ }
+ }
+
+ printf( "\n" );
+
+ return( 0 );
+}
+
+#endif
+
diff --git a/src/aes_utils.h b/src/aes_utils.h
new file mode 100644
index 0000000..af81401
--- /dev/null
+++ b/src/aes_utils.h
@@ -0,0 +1,24 @@
+#ifndef _AES_H
+#define _AES_H
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+#ifndef uint32
+#define uint32 unsigned long int
+#endif
+
+typedef struct
+{
+ uint32 erk[64]; /* encryption round keys */
+ uint32 drk[64]; /* decryption round keys */
+ int nr; /* number of rounds */
+}
+aes_context;
+
+int aes_set_key( aes_context *ctx, uint8 *key, int nbits );
+void aes_encrypt( aes_context *ctx, uint8 input[16], uint8 output[16] );
+void aes_decrypt( aes_context *ctx, uint8 input[16], uint8 output[16] );
+
+#endif /* aes.h */
diff --git a/src/base64.c b/src/base64.c
new file mode 100644
index 0000000..a2d7f76
--- /dev/null
+++ b/src/base64.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``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 THE INSTITUTE 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include
+RCSID("$Id: base64.c,v 1.1.1.1 2005/07/23 13:57:04 shiro Exp $");
+#endif
+#include
+#include
+#include "base64.h"
+
+static char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int
+pos(char c)
+{
+ char *p;
+ for (p = base64_chars; *p; p++)
+ if (*p == c)
+ return p - base64_chars;
+ return -1;
+}
+
+int
+base64_encode(const void *data, int size, char **str)
+{
+ char *s, *p;
+ int i;
+ int c;
+ const unsigned char *q;
+
+ p = s = (char *) malloc(size * 4 / 3 + 4);
+ if (p == NULL)
+ return -1;
+ q = (const unsigned char *) data;
+ i = 0;
+ for (i = 0; i < size;) {
+ c = q[i++];
+ c *= 256;
+ if (i < size)
+ c += q[i];
+ i++;
+ c *= 256;
+ if (i < size)
+ c += q[i];
+ i++;
+ p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+ p[1] = base64_chars[(c & 0x0003f000) >> 12];
+ p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+ p[3] = base64_chars[(c & 0x0000003f) >> 0];
+ if (i > size)
+ p[3] = '=';
+ if (i > size + 1)
+ p[2] = '=';
+ p += 4;
+ }
+ *p = 0;
+ *str = s;
+ return strlen(s);
+}
+
+#define DECODE_ERROR 0xffffffff
+
+static unsigned int
+token_decode(const char *token)
+{
+ int i;
+ unsigned int val = 0;
+ int marker = 0;
+ if (strlen(token) < 4)
+ return DECODE_ERROR;
+ for (i = 0; i < 4; i++) {
+ val *= 64;
+ if (token[i] == '=')
+ marker++;
+ else if (marker > 0)
+ return DECODE_ERROR;
+ else
+ val += pos(token[i]);
+ }
+ if (marker > 2)
+ return DECODE_ERROR;
+ return (marker << 24) | val;
+}
+
+int
+base64_decode(const char *str, void *data)
+{
+ const char *p;
+ unsigned char *q;
+
+ q = (unsigned char*)data;
+ for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
+ unsigned int val = token_decode(p);
+ unsigned int marker = (val >> 24) & 0xff;
+ if (val == DECODE_ERROR)
+ return -1;
+ *q++ = (val >> 16) & 0xff;
+ if (marker < 2)
+ *q++ = (val >> 8) & 0xff;
+ if (marker < 1)
+ *q++ = val & 0xff;
+ }
+ return q - (unsigned char *) data;
+}
diff --git a/src/base64.h b/src/base64.h
new file mode 100644
index 0000000..2a6919c
--- /dev/null
+++ b/src/base64.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * 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.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``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 THE INSTITUTE 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.
+ */
+
+/* $Id: base64.h,v 1.1.1.1 2005/07/23 13:57:04 shiro Exp $ */
+
+#ifndef _BASE64_H_
+#define _BASE64_H_
+
+int base64_encode(const void *data, int size, char **str);
+int base64_decode(const char *str, void *data);
+
+#endif
diff --git a/src/codec.cc b/src/codec.cc
new file mode 100644
index 0000000..21beaff
--- /dev/null
+++ b/src/codec.cc
@@ -0,0 +1,144 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "aes_utils.h"
+#include "base64.h"
+
+#include "../alac/ALACEncoder.h"
+#include "../alac/ALACBitUtilities.h"
+
+using namespace v8;
+using namespace node;
+
+static int kBlockSize = 16;
+static int kFramesPerPacket = 352;
+
+namespace nodeairtunes {
+
+void FillInputAudioFormat(AudioFormatDescription *format) {
+ format->mFormatID = kALACFormatLinearPCM;
+ format->mSampleRate = 44100;
+ format->mFormatFlags = 12;
+
+ format->mBytesPerPacket = 4;
+ format->mBytesPerFrame = 4;
+ format->mBitsPerChannel = 16;
+ format->mChannelsPerFrame = 2;
+ format->mFramesPerPacket = 1;
+
+ format->mReserved = 0;
+}
+
+void FillOutputAudioFormat(AudioFormatDescription *format) {
+ format->mFormatID = kALACFormatAppleLossless;
+ format->mSampleRate = 44100;
+ format->mFormatFlags = 1;
+
+ format->mBytesPerPacket = 0;
+ format->mBytesPerFrame = 0;
+ format->mBitsPerChannel = 0;
+ format->mChannelsPerFrame = 2;
+ format->mFramesPerPacket = kFramesPerPacket;
+
+ format->mReserved = 0;
+}
+
+void encoder_weak_callback (Persistent wrapper, void *arg) {
+ HandleScope scope;
+ ALACEncoder *encoder = (ALACEncoder *)arg;
+ delete encoder;
+ wrapper.Dispose();
+}
+
+
+static int AESEncrypt(uint8_t *data, int size)
+{
+ uint8_t *buf;
+ int i = 0, j;
+ uint8_t iv [] = { 0x78, 0xf4, 0x41, 0x2c, 0x8d, 0x17, 0x37, 0x90, 0x2b, 0x15, 0xa6, 0xb3, 0xee, 0x77, 0x0d, 0x67 };
+ uint8_t nv [] = { 0x78, 0xf4, 0x41, 0x2c, 0x8d, 0x17, 0x37, 0x90, 0x2b, 0x15, 0xa6, 0xb3, 0xee, 0x77, 0x0d, 0x67 };
+ uint8_t aes_key [] = { 0x14, 0x49, 0x7d, 0xcc, 0x98, 0xe1, 0x37, 0xa8, 0x55, 0xc1, 0x45, 0x5a, 0x6b, 0xc0, 0xc9, 0x79};
+
+ aes_context ctx;
+ aes_set_key(&ctx, aes_key, 128);
+ memcpy(nv, iv, kBlockSize);
+
+ while(i + kBlockSize <= size) {
+ buf = data + i;
+
+ for(j = 0; j < kBlockSize; j++)
+ buf[j] ^= nv[j];
+
+ aes_encrypt(&ctx, buf, buf);
+ memcpy(nv, buf, kBlockSize);
+
+ i += kBlockSize;
+ }
+
+ return i;
+}
+
+Handle NewEncoder(const Arguments& args) {
+ HandleScope scope;
+
+ AudioFormatDescription outputFormat;
+ FillOutputAudioFormat(&outputFormat);
+
+ ALACEncoder *encoder = new ALACEncoder();
+
+ encoder->SetFrameSize(kFramesPerPacket);
+ encoder->InitializeEncoder(outputFormat);
+
+ Persistent encoderClass = Persistent::New(ObjectTemplate::New());
+ encoderClass->SetInternalFieldCount(1);
+ Persistent