poke/alac/codec/ag_dec.c

363 lines
8.2 KiB
C
Raw Permalink Normal View History

2023-02-12 10:16:19 +01:00
/*
* 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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if __GNUC__ && TARGET_OS_MAC
#if __POWERPC__
#include <ppc_intrinsics.h>
#else
#include <libkern/OSByteOrder.h>
#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<<params->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<MAX_PREFIX_32);
//Assert(result+1+k <= 32);
tempbits += result;
tempbits += 1;
if (k != 1)
{
streamlong <<= result+1;
v = get_next_fromlong(streamlong, k);
tempbits += k;
tempbits -= 1;
result = result*m;
if(v>=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<<k)-1;
n = dyn_get_32bit( in, &bitPos, m, k, maxSize );
// least significant bit is sign bit
{
uint32_t ndecode = n + zmode;
int32_t multiplier = (- (ndecode&1));
multiplier |= 1;
del = ((ndecode+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<<k)-1) & wb_local;
n = dyn_get(in, &bitPos, mz, k);
RequireAction(c+n <= numSamples, status = kALAC_ParamError; goto Exit; );
for(j=0; j < n; j++)
{
*outPtr++ = 0;
++c;
}
if(n >= 65535)
zmode = 0;
mb = 0;
}
}
Exit:
*outNumBits = (bitPos - startPos);
BitBufferAdvance( bitstream, *outNumBits );
RequireAction( bitstream->cur <= bitstream->end, status = kALAC_ParamError; );
return status;
}