#include "tak1.h" #include #include #include #include #include namespace { [[noreturn]] void invalid(const char* reason) { throw std::runtime_error(std::string("TAK 1.0: ") + reason); } void require(bool ok, const char* reason) { if (!ok) invalid(reason); } int32_t wrap(uint32_t x) { int32_t v; std::memcpy(&v, &x, 4); return v; } uint32_t magnitude(int32_t x) { return x < 0 ? 0U - uint32_t(x) : uint32_t(x); } int32_t truncate(int32_t x, int shift) { uint32_t v = magnitude(x) >> shift; return wrap(x < 0 ? 0U - v : v); } int32_t predict(uint32_t sum, int shift) { int32_t signedSum = wrap(sum); uint32_t value = (magnitude(signedSum) + (1U << (shift - 1))) >> shift; value = std::min(value, 8191U); return signedSum < 0 ? -int32_t(value) : int32_t(value); } class Bits { public: Bits(const uint8_t* data, size_t size) : data(data), size(size) {} uint32_t get(int n) { require(n >= 0 && n <= 32 && size <= SIZE_MAX / 8 && pos <= size*8 && size_t(n) <= size*8-pos, "truncated or invalid bitstream"); uint64_t value = 0; const size_t first = pos / 8, end = (pos + n + 7) / 8; for (size_t i = first; i < end; ++i) value |= uint64_t(data[i]) << ((i-first)*8); value >>= pos & 7; pos += n; return uint32_t(value & (n == 32 ? UINT32_MAX : ((uint64_t(1)< 0 && n <= 32, "invalid signed value size"); uint32_t v = get(n), sign = 1U << (n-1); return wrap((v ^ sign)-sign); } int unary(int limit) { for (int i=0; i 0 && i.frameSamples <= maximum && i.frameSamples <= 16384, "invalid frame sample count"); require(i.bits >= 8 && i.bits <= 24 && i.channels <= 2, "unsupported PCM format"); return i; } uint32_t crc24(const uint8_t* data, size_t size) { static const auto table = [] { std::array t{}; for (unsigned i=0; i<256; ++i) { uint32_t v = i << 16; for (int j=0; j<8; ++j) v = ((v<<1) ^ ((v & 0x800000U) ? 0x864CFBU : 0)) & 0xFFFFFFU; t[i] = v; } return t; }(); uint32_t crc = 0xB704CEU; for (size_t i=0; i>16)^data[i])&255]) & 0xFFFFFFU; return crc; } void checkCrc(const uint8_t* data, size_t size) { require(size >= 3, "missing checksum"); uint32_t stored = uint32_t(data[size-3]) | uint32_t(data[size-2])<<8 | uint32_t(data[size-1])<<16; require(crc24(data,size-3) == stored, "frame checksum mismatch"); } struct Code { int width, outer, inner, extended, step, bias; }; // Numeric codebook parameters recovered from the TAK 1.0 decoder interface. constexpr Code codes[47] = { {3,6,4,8,4,44}, {4,14,10,18,4,54}, {4,12,4,20,8,92}, {5,28,22,34,8,106}, {5,26,12,40,12,148}, {6,56,44,68,16,212}, {6,52,24,80,24,296}, {7,112,88,136,32,424}, {7,104,48,160,48,592}, {8,224,176,272,64,848}, {8,208,96,320,96,1184}, {9,448,352,544,128,1696}, {9,416,192,640,192,2368}, {10,896,704,1088,256,3392}, {10,832,384,1280,384,4736}, {11,1792,1408,2176,512,6784}, {11,1664,768,2560,768,9472}, {12,3584,2816,4352,1024,13568}, {12,3328,1536,5120,1536,18944}, {13,7168,5632,8704,2048,27136}, {13,6656,3072,10240,3072,37888}, {14,14336,11264,17408,4096,54272}, {14,13312,6144,20480,6144,75776}, {15,28672,22528,34816,8192,108544}, {15,26624,12288,40960,12288,151552}, {16,57344,45056,69632,16384,217088}, {16,53248,24576,81920,24576,303104}, {17,114688,90112,139264,32768,434176}, {17,106496,49152,163840,49152,606208}, {18,229376,180224,278528,65536,868352}, {18,212992,98304,327680,98304,1212416}, {19,458752,360448,557056,131072,1736704}, {19,425984,196608,655360,196608,2424832}, {20,917504,720896,1114112,262144,3473408}, {20,851968,393216,1310720,393216,4849664}, {21,1835008,1441792,2228224,524288,6946816}, {21,1703936,786432,2621440,786432,9699328}, {22,3670016,2883584,4456448,1048576,13893632}, {22,3407872,1572864,5242880,1572864,19398656}, {23,7340032,5767168,8912896,2097152,27787264}, {23,6815744,3145728,10485760,3145728,38797312}, {24,14680064,11534336,17825792,4194304,55574528}, {24,13631488,6291456,20971520,6291456,77594624}, {25,29360128,23068672,35651584,8388608,111149056}, {25,27262976,12582912,41943040,12582912,155189248}, {26,58720256,46137344,71303168,16777216,222298112}, {26,54525952,25165824,83886080,25165824,310378496}, }; constexpr int orders[16] = {4,8,12,16,24,32,48,64,80,96,112,128,160,192,224,256}; int ceilLog2(uint32_t value) { if (value <= 1) return int(value); int n=0; for (--value; value; value>>=1) ++n; return n; } std::vector boundaries(int rate, int samples, int alignment, int divider, int maxCount) { std::vector t{0}; for (int i=1; i<=maxCount; ++i) { int v = (rate*i/divider+alignment/2) & -alignment; t.push_back(v); if (v >= samples) break; } if (t.back() < samples) t.back() = samples; else if (t.back() > samples) { t.back() = samples; if (t.back()-t[t.size()-2] < alignment) t.erase(t.end()-2); } return t; } struct Filter { std::vector coefficients; int quant = 0, shift = 0; }; struct Channel { std::vector data; int shift = 0; Filter prefilter; }; class Payload { public: Payload(const uint8_t* data, size_t bytes, const Tak1Decoder::Info& info, int samples) : bits(data,bytes), info(info), n(samples), channelTable(boundaries(info.rate,samples,8,256,64)), residueTable(boundaries(info.rate,samples,4,512,128)) {} Tak1Decoder::Audio decode() { Tak1Decoder::Audio audio; audio.samples = n; Channel ch[2]; if (n < 16) { for (int c=0; c= 3) require(ch[0].prefilter.coefficients.empty() && ch[1].prefilter.coefficients.empty(), "incompatible stereo prefilter"); joint(mode,ch,jointFilter,jointHead,jointTail); } for (int c=0; c channelTable, residueTable; void segment(int mode, int escapeBits, int32_t* output, int count) { require(mode >= 0 && mode <= 50 && count >= 0 && count <= n, "invalid residual segment"); if (!mode) { std::fill(output,output+count,0); return; } for (int i=0; i 0 && escapeBits <= 31, "invalid residual escape"); v = bits.get(escapeBits); uint32_t half = 1U << (escapeBits-1); output[i] = wrap(v < half ? v+base+4 : 0U-(v+base+4-half)); } } } else { const auto& code = codes[mode-4]; v = bits.get(code.width); if (int(v) < code.inner) output[i] = wrap((v>>1) ^ (0U-(v&1))); else { uint32_t sign = bits.get(1); if (int(v) < code.outer) v += code.inner/2-code.inner; else { int q = bits.unary(9); if (q < 9) v += code.extended/2-code.outer + uint32_t(code.step/2)*q; else { require(escapeBits <= 42, "invalid residual escape"); uint32_t extra = escapeBits > 10 ? bits.get(escapeBits-10) : 0; v += code.bias/2-code.outer + uint32_t(code.step/2)*extra; } } output[i] = wrap(v ^ (0U-sign)); } } } } void residues(int32_t* output, int count) { require(count >= 0 && count <= n, "invalid residual count"); if (!bits.get(1)) { int mode = int(bits.get(6)); int escape = mode ? int(bits.get(5)) : 1; require(!mode || (escape > 0 && escape <= 28), "invalid residual escape"); segment(mode,escape,output,count); return; } int parts = 0; while (parts+1 < int(residueTable.size()) && residueTable[parts+1] <= count) ++parts; if (!parts || count-residueTable[parts] >= 4) ++parts; require(parts > 1 && parts <= 128 && parts < int(residueTable.size()), "invalid residual partition count"); std::array modes{}; int format = int(bits.get(2)); if (!format) { int base = int(bits.get(6)), width = int(bits.get(3))+1; for (int i=0; i= 0 && mode <= 50, "invalid residual coding mode"); while (i 0 && mode < 4) { int base = 1 << (mode-1); if (limit > uint32_t(base+3)) escape = ceilLog2(limit-base-3)+1; } else if (mode >= 4) { const auto& code = codes[mode-4]; escape = limit*2 < uint32_t(code.bias+code.step) ? 10 : ceilLog2((limit*2-code.bias)/code.step+1)+10; } segment(mode,escape,output+residueTable[start],length); } } Filter coefficients(int count) { require(count > 0 && count <= 256 && !(count&3), "invalid predictor order"); Filter f; f.quant = int(bits.get(3)); bool differential = count >= 12 && bits.get(1); f.coefficients.resize(count); int base=0, deltaBits=0; if (count > 16) { base = int(bits.get(4)); deltaBits = int(bits.get(1))+2; } for (int i=0; i 0 && width <= 15, "invalid predictor coefficient size"); for (int j=0; j<4; ++j) f.coefficients[i+j] = bits.signedValue(width); } if (differential) { for (int i=1; i= order, "predictor exceeds subframe"); std::vector history(count); for (int i=0; i 0 && index < int(channelTable.size())-1, "invalid channel partition index"); offsets[i] = channelTable[index]; require(offsets[i] > offsets[i-1], "unordered channel partitions"); } offsets[parts] = n-1; for (int i=0; i 0, "empty channel partition"); subframe(c.data.data()+1+offsets[i],length); } return c; } void restore(Channel& c) { if (!c.prefilter.coefficients.empty()) fir(c.data.data()+1,n-1,c.prefilter); for (int i=1; i= 256, "stereo predictor needs a longer frame"); const auto& source = mode == 1 ? left : right; auto& target = mode == 1 ? right : left; const int start=4, body=n-1-7; for (int i=0; i>1) | (difference & 0x80000000U)); right[i] = wrap((sum>>1) | (sum & 0x80000000U)); } } } } }; } Tak1Decoder::Tak1Decoder(const uint8_t* data, size_t size) { Bits b(data,size); initial = current = streamInfo(b); } Tak1Decoder::Audio Tak1Decoder::decode(const uint8_t* packet, size_t size) { Bits b(packet,size); Info frameInfo = current; require(b.get(16) == 0xA0FF, "missing frame sync"); uint32_t flags = b.get(3), number = b.get(21); int samples = 0; if (flags&1) { samples = int(b.get(14))+1; require(b.get(2) == 0, "invalid last frame flags"); } if (flags&2) { Info parsed = streamInfo(b); require(parsed.rate == initial.rate && parsed.bits == initial.bits && parsed.channels == initial.channels, "PCM format changes inside the track"); frameInfo = parsed; if (b.get(6)) b.get(25); b.align(); } require(!(flags&4), "unsupported per-frame metadata"); b.get(24); size_t header = b.position()/8; checkCrc(packet,header); // Verify compressed data before allowing it to drive allocations or filters. checkCrc(packet+header,size-header); samples = samples ? samples : frameInfo.frameSamples; require(samples > 0 && samples <= frameInfo.frameSamples, "invalid last frame size"); Payload payload(packet+header,size-header,frameInfo,samples); Audio audio = payload.decode(); size_t bytes = payload.consumedBytes()+3; require(bytes == size-header, "unexpected frame payload length"); audio.frameNumber = number; current = frameInfo; return audio; }