V1.0
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <oi_codec_sbc_private.h>
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
PRIVATE OI_STATUS OI_CODEC_SBC_Alloc(OI_CODEC_SBC_COMMON_CONTEXT *common,
|
||||
OI_UINT32 *codecDataAligned,
|
||||
OI_UINT32 codecDataBytes,
|
||||
OI_UINT8 maxChannels,
|
||||
OI_UINT8 pcmStride)
|
||||
{
|
||||
int i;
|
||||
size_t filterBufferCount;
|
||||
size_t subdataSize;
|
||||
OI_BYTE *codecData = (OI_BYTE*)codecDataAligned;
|
||||
|
||||
if ((maxChannels < 1) || (maxChannels > 2)) {
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if ((pcmStride < 1) || (pcmStride > maxChannels)) {
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
common->maxChannels = maxChannels;
|
||||
common->pcmStride = pcmStride;
|
||||
|
||||
/* Compute sizes needed for the memory regions, and bail if we don't have
|
||||
* enough memory for them. */
|
||||
subdataSize = maxChannels * sizeof(common->subdata[0]) * SBC_MAX_BANDS * SBC_MAX_BLOCKS;
|
||||
if (subdataSize > codecDataBytes) {
|
||||
return OI_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
filterBufferCount = (codecDataBytes - subdataSize) / (sizeof(common->filterBuffer[0][0]) * SBC_MAX_BANDS * maxChannels);
|
||||
if (filterBufferCount < SBC_CODEC_MIN_FILTER_BUFFERS) {
|
||||
return OI_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
common->filterBufferLen = (uint32_t) filterBufferCount * SBC_MAX_BANDS;
|
||||
|
||||
/* Allocate memory for the subband data */
|
||||
common->subdata = (OI_INT32*)codecData;
|
||||
codecData += subdataSize;
|
||||
OI_ASSERT(codecDataBytes >= subdataSize);
|
||||
codecDataBytes -= (uint32_t) subdataSize;
|
||||
|
||||
/* Allocate memory for the synthesis buffers */
|
||||
for (i = 0; i < maxChannels; ++i) {
|
||||
size_t allocSize = common->filterBufferLen * sizeof(common->filterBuffer[0][0]);
|
||||
common->filterBuffer[i] = (SBC_BUFFER_T*)codecData;
|
||||
OI_ASSERT(codecDataBytes >= allocSize);
|
||||
codecData += allocSize;
|
||||
codecDataBytes -= (uint32_t) allocSize;
|
||||
}
|
||||
|
||||
return OI_OK;
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/** @file
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**@addgroup codec_internal*/
|
||||
/**@{*/
|
||||
|
||||
#include <oi_codec_sbc_private.h>
|
||||
|
||||
static void dualBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common)
|
||||
{
|
||||
OI_UINT bitcountL;
|
||||
OI_UINT bitcountR;
|
||||
OI_UINT bitpoolPreferenceL = 0;
|
||||
OI_UINT bitpoolPreferenceR = 0;
|
||||
BITNEED_UNION1 bitneedsL;
|
||||
BITNEED_UNION1 bitneedsR;
|
||||
|
||||
bitcountL = computeBitneed(common, bitneedsL.uint8, 0, &bitpoolPreferenceL);
|
||||
bitcountR = computeBitneed(common, bitneedsR.uint8, 1, &bitpoolPreferenceR);
|
||||
|
||||
oneChannelBitAllocation(common, &bitneedsL, 0, bitcountL);
|
||||
oneChannelBitAllocation(common, &bitneedsR, 1, bitcountR);
|
||||
}
|
||||
|
||||
static void stereoBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common)
|
||||
{
|
||||
const OI_UINT nrof_subbands = common->frameInfo.nrof_subbands;
|
||||
BITNEED_UNION2 bitneeds;
|
||||
OI_UINT excess;
|
||||
OI_INT bitadjust;
|
||||
OI_UINT bitcount;
|
||||
OI_UINT sbL;
|
||||
OI_UINT sbR;
|
||||
OI_UINT bitpoolPreference = 0;
|
||||
|
||||
bitcount = computeBitneed(common, &bitneeds.uint8[0], 0, &bitpoolPreference);
|
||||
bitcount += computeBitneed(common, &bitneeds.uint8[nrof_subbands], 1, &bitpoolPreference);
|
||||
|
||||
{
|
||||
OI_UINT ex;
|
||||
bitadjust = adjustToFitBitpool(common->frameInfo.bitpool, bitneeds.uint32, 2 * nrof_subbands, bitcount, &ex);
|
||||
/* We want the compiler to put excess into a register */
|
||||
excess = ex;
|
||||
}
|
||||
sbL = 0;
|
||||
sbR = nrof_subbands;
|
||||
while (sbL < nrof_subbands) {
|
||||
excess = allocAdjustedBits(&common->bits.uint8[sbL], bitneeds.uint8[sbL] + bitadjust, excess);
|
||||
++sbL;
|
||||
excess = allocAdjustedBits(&common->bits.uint8[sbR], bitneeds.uint8[sbR] + bitadjust, excess);
|
||||
++sbR;
|
||||
}
|
||||
sbL = 0;
|
||||
sbR = nrof_subbands;
|
||||
while (excess) {
|
||||
excess = allocExcessBits(&common->bits.uint8[sbL], excess);
|
||||
++sbL;
|
||||
if (!excess) {
|
||||
break;
|
||||
}
|
||||
excess = allocExcessBits(&common->bits.uint8[sbR], excess);
|
||||
++sbR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const BIT_ALLOC balloc[] = {
|
||||
monoBitAllocation, /* SBC_MONO */
|
||||
dualBitAllocation, /* SBC_DUAL_CHANNEL */
|
||||
stereoBitAllocation, /* SBC_STEREO */
|
||||
stereoBitAllocation /* SBC_JOINT_STEREO */
|
||||
};
|
||||
|
||||
PRIVATE void OI_SBC_ComputeBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common)
|
||||
{
|
||||
OI_ASSERT(common->frameInfo.bitpool <= OI_SBC_MaxBitpool(&common->frameInfo));
|
||||
OI_ASSERT(common->frameInfo.mode < OI_ARRAYSIZE(balloc));
|
||||
|
||||
/*
|
||||
* Using an array of function pointers prevents the compiler from creating a suboptimal
|
||||
* monolithic inlined bit allocation function.
|
||||
*/
|
||||
balloc[common->frameInfo.mode](common);
|
||||
}
|
||||
|
||||
OI_UINT32 OI_CODEC_SBC_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame)
|
||||
{
|
||||
return internal_CalculateBitrate(frame);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the current maximum bitneed and clear it.
|
||||
*/
|
||||
#if 0
|
||||
OI_UINT8 OI_CODEC_SBC_GetMaxBitneed(OI_CODEC_SBC_COMMON_CONTEXT *common)
|
||||
{
|
||||
OI_UINT8 max = common->maxBitneed;
|
||||
|
||||
common->maxBitneed = 0;
|
||||
return max;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Calculates the bitpool size for a given frame length
|
||||
*/
|
||||
OI_UINT16 OI_CODEC_SBC_CalculateBitpool(OI_CODEC_SBC_FRAME_INFO *frame,
|
||||
OI_UINT16 frameLen)
|
||||
{
|
||||
OI_UINT16 nrof_subbands = frame->nrof_subbands;
|
||||
OI_UINT16 nrof_blocks = frame->nrof_blocks;
|
||||
OI_UINT16 hdr;
|
||||
OI_UINT16 bits;
|
||||
|
||||
if (frame->mode == SBC_JOINT_STEREO) {
|
||||
hdr = 9 * nrof_subbands;
|
||||
} else {
|
||||
if (frame->mode == SBC_MONO) {
|
||||
hdr = 4 * nrof_subbands;
|
||||
} else {
|
||||
hdr = 8 * nrof_subbands;
|
||||
}
|
||||
if (frame->mode == SBC_DUAL_CHANNEL) {
|
||||
nrof_blocks *= 2;
|
||||
}
|
||||
}
|
||||
bits = (8 * (frameLen - SBC_HEADER_LEN)) - hdr;
|
||||
return DIVIDE(bits, nrof_blocks);
|
||||
}
|
||||
|
||||
OI_UINT16 OI_CODEC_SBC_CalculatePcmBytes(OI_CODEC_SBC_COMMON_CONTEXT *common)
|
||||
{
|
||||
return sizeof(OI_INT16) * common->pcmStride * common->frameInfo.nrof_subbands * common->frameInfo.nrof_blocks;
|
||||
}
|
||||
|
||||
|
||||
OI_UINT16 OI_CODEC_SBC_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame)
|
||||
{
|
||||
return internal_CalculateFramelen(frame);
|
||||
}
|
||||
|
||||
/**@}*/
|
||||
@@ -0,0 +1,394 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/**
|
||||
@file
|
||||
|
||||
The functions in this file relate to the allocation of available bits to
|
||||
subbands within the SBC/eSBC frame, along with support functions for computing
|
||||
frame length and bitrate.
|
||||
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**
|
||||
@addtogroup codec_internal
|
||||
@{
|
||||
*/
|
||||
|
||||
#include "oi_utils.h"
|
||||
#include <oi_codec_sbc_private.h>
|
||||
|
||||
OI_UINT32 OI_SBC_MaxBitpool(OI_CODEC_SBC_FRAME_INFO *frame)
|
||||
{
|
||||
switch (frame->mode) {
|
||||
case SBC_MONO:
|
||||
case SBC_DUAL_CHANNEL:
|
||||
return 16 * frame->nrof_subbands;
|
||||
case SBC_STEREO:
|
||||
case SBC_JOINT_STEREO:
|
||||
return 32 * frame->nrof_subbands;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ERROR(("Invalid frame mode %d", frame->mode));
|
||||
OI_ASSERT(FALSE);
|
||||
return 0; /* Should never be reached */
|
||||
}
|
||||
|
||||
|
||||
PRIVATE OI_UINT16 internal_CalculateFramelen(OI_CODEC_SBC_FRAME_INFO *frame)
|
||||
{
|
||||
OI_UINT16 nbits = frame->nrof_blocks * frame->bitpool;
|
||||
OI_UINT16 nrof_subbands = frame->nrof_subbands;
|
||||
OI_UINT16 result = nbits;
|
||||
|
||||
if (frame->mode == SBC_JOINT_STEREO) {
|
||||
result += nrof_subbands + (8 * nrof_subbands);
|
||||
} else {
|
||||
if (frame->mode == SBC_DUAL_CHANNEL) { result += nbits; }
|
||||
if (frame->mode == SBC_MONO) { result += 4*nrof_subbands; } else { result += 8*nrof_subbands; }
|
||||
}
|
||||
return SBC_HEADER_LEN + ((result + 7) / 8);
|
||||
}
|
||||
|
||||
|
||||
PRIVATE OI_UINT32 internal_CalculateBitrate(OI_CODEC_SBC_FRAME_INFO *frame)
|
||||
{
|
||||
OI_UINT blocksbands;
|
||||
blocksbands = frame->nrof_subbands * frame->nrof_blocks;
|
||||
|
||||
return DIVIDE(8 * internal_CalculateFramelen(frame) * frame->frequency, blocksbands);
|
||||
}
|
||||
|
||||
|
||||
INLINE OI_UINT16 OI_SBC_CalculateFrameAndHeaderlen(OI_CODEC_SBC_FRAME_INFO *frame, OI_UINT *headerLen_)
|
||||
{
|
||||
OI_UINT headerLen = SBC_HEADER_LEN + (frame->nrof_subbands * frame->nrof_channels/2);
|
||||
|
||||
if (frame->mode == SBC_JOINT_STEREO) { headerLen++; }
|
||||
|
||||
*headerLen_ = headerLen;
|
||||
return internal_CalculateFramelen(frame);
|
||||
}
|
||||
|
||||
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
|
||||
/*
|
||||
* Computes the bit need for each sample and as also returns a counts of bit needs that are greater
|
||||
* than one. This count is used in the first phase of bit allocation.
|
||||
*
|
||||
* We also compute a preferred bitpool value that this is the minimum bitpool needed to guarantee
|
||||
* lossless representation of the audio data. The preferred bitpool may be larger than the bits
|
||||
* actually required but the only input we have are the scale factors. For example, it takes 2 bits
|
||||
* to represent values in the range -1 .. +1 but the scale factor is 0. To guarantee lossless
|
||||
* representation we add 2 to each scale factor and sum them to come up with the preferred bitpool.
|
||||
* This is not ideal because 0 requires 0 bits but we currently have no way of knowing this.
|
||||
*
|
||||
* @param bitneed Array to return bitneeds for each subband
|
||||
*
|
||||
* @param ch Channel 0 or 1
|
||||
*
|
||||
* @param preferredBitpool Returns the number of reserved bits
|
||||
*
|
||||
* @return The SBC bit need
|
||||
*
|
||||
*/
|
||||
OI_UINT computeBitneed(OI_CODEC_SBC_COMMON_CONTEXT *common,
|
||||
OI_UINT8 *bitneeds,
|
||||
OI_UINT ch,
|
||||
OI_UINT *preferredBitpool)
|
||||
{
|
||||
static const OI_INT8 offset4[4][4] = {
|
||||
{ -1, 0, 0, 0 },
|
||||
{ -2, 0, 0, 1 },
|
||||
{ -2, 0, 0, 1 },
|
||||
{ -2, 0, 0, 1 }
|
||||
};
|
||||
|
||||
static const OI_INT8 offset8[4][8] = {
|
||||
{ -2, 0, 0, 0, 0, 0, 0, 1 },
|
||||
{ -3, 0, 0, 0, 0, 0, 1, 2 },
|
||||
{ -4, 0, 0, 0, 0, 0, 1, 2 },
|
||||
{ -4, 0, 0, 0, 0, 0, 1, 2 }
|
||||
};
|
||||
|
||||
const OI_UINT nrof_subbands = common->frameInfo.nrof_subbands;
|
||||
OI_UINT sb;
|
||||
OI_INT8 *scale_factor = &common->scale_factor[ch ? nrof_subbands : 0];
|
||||
OI_UINT bitcount = 0;
|
||||
OI_UINT8 maxBits = 0;
|
||||
OI_UINT8 prefBits = 0;
|
||||
|
||||
if (common->frameInfo.alloc == SBC_SNR) {
|
||||
for (sb = 0; sb < nrof_subbands; sb++) {
|
||||
OI_INT bits = scale_factor[sb];
|
||||
if (bits > maxBits) {
|
||||
maxBits = bits;
|
||||
}
|
||||
if ((bitneeds[sb] = bits) > 1) {
|
||||
bitcount += bits;
|
||||
}
|
||||
prefBits += 2 + bits;
|
||||
}
|
||||
} else {
|
||||
const OI_INT8 *offset;
|
||||
if (nrof_subbands == 4) {
|
||||
offset = offset4[common->frameInfo.freqIndex];
|
||||
} else {
|
||||
offset = offset8[common->frameInfo.freqIndex];
|
||||
}
|
||||
for (sb = 0; sb < nrof_subbands; sb++) {
|
||||
OI_INT bits = scale_factor[sb];
|
||||
if (bits > maxBits) {
|
||||
maxBits = bits;
|
||||
}
|
||||
prefBits += 2 + bits;
|
||||
if (bits) {
|
||||
bits -= offset[sb];
|
||||
if (bits > 0) {
|
||||
bits /= 2;
|
||||
}
|
||||
bits += 5;
|
||||
}
|
||||
if ((bitneeds[sb] = bits) > 1) {
|
||||
bitcount += bits;
|
||||
}
|
||||
}
|
||||
}
|
||||
common->maxBitneed = OI_MAX(maxBits, common->maxBitneed);
|
||||
*preferredBitpool += prefBits;
|
||||
return bitcount;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Explanation of the adjustToFitBitpool inner loop.
|
||||
*
|
||||
* The inner loop computes the effect of adjusting the bit allocation up or
|
||||
* down. Allocations must be 0 or in the range 2..16. This is accomplished by
|
||||
* the following code:
|
||||
*
|
||||
* for (s = bands - 1; s >= 0; --s) {
|
||||
* OI_INT bits = bitadjust + bitneeds[s];
|
||||
* bits = bits < 2 ? 0 : bits;
|
||||
* bits = bits > 16 ? 16 : bits;
|
||||
* count += bits;
|
||||
* }
|
||||
*
|
||||
* This loop can be optimized to perform 4 operations at a time as follows:
|
||||
*
|
||||
* Adjustment is computed as a 7 bit signed value and added to the bitneed.
|
||||
*
|
||||
* Negative allocations are zeroed by masking. (n & 0x40) >> 6 puts the
|
||||
* sign bit into bit 0, adding this to 0x7F give us a mask of 0x80
|
||||
* for -ve values and 0x7F for +ve values.
|
||||
*
|
||||
* n &= 0x7F + (n & 0x40) >> 6)
|
||||
*
|
||||
* Allocations greater than 16 are truncated to 16. Adjusted allocations are in
|
||||
* the range 0..31 so we know that bit 4 indicates values >= 16. We use this bit
|
||||
* to create a mask that zeroes bits 0 .. 3 if bit 4 is set.
|
||||
*
|
||||
* n &= (15 + (n >> 4))
|
||||
*
|
||||
* Allocations of 1 are disallowed. Add and shift creates a mask that
|
||||
* eliminates the illegal value
|
||||
*
|
||||
* n &= ((n + 14) >> 4) | 0x1E
|
||||
*
|
||||
* These operations can be performed in 8 bits without overflowing so we can
|
||||
* operate on 4 values at once.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Encoder/Decoder
|
||||
*
|
||||
* Computes adjustment +/- of bitneeds to fill bitpool and returns overall
|
||||
* adjustment and excess bits.
|
||||
*
|
||||
* @param bitpool The bitpool we have to work within
|
||||
*
|
||||
* @param bitneeds An array of bit needs (more acturately allocation prioritities) for each
|
||||
* subband across all blocks in the SBC frame
|
||||
*
|
||||
* @param subbands The number of subbands over which the adkustment is calculated. For mono and
|
||||
* dual mode this is 4 or 8, for stereo or joint stereo this is 8 or 16.
|
||||
*
|
||||
* @param bitcount A starting point for the adjustment
|
||||
*
|
||||
* @param excess Returns the excess bits after the adjustment
|
||||
*
|
||||
* @return The adjustment.
|
||||
*/
|
||||
OI_INT adjustToFitBitpool(const OI_UINT bitpool,
|
||||
OI_UINT32 *bitneeds,
|
||||
const OI_UINT subbands,
|
||||
OI_UINT bitcount,
|
||||
OI_UINT *excess)
|
||||
{
|
||||
OI_INT maxBitadjust = 0;
|
||||
OI_INT bitadjust = (bitcount > bitpool) ? -8 : 8;
|
||||
OI_INT chop = 8;
|
||||
|
||||
/*
|
||||
* This is essentially a binary search for the optimal adjustment value.
|
||||
*/
|
||||
while ((bitcount != bitpool) && chop) {
|
||||
OI_UINT32 total = 0;
|
||||
OI_UINT count;
|
||||
OI_UINT32 adjust4;
|
||||
OI_INT i;
|
||||
|
||||
adjust4 = bitadjust & 0x7F;
|
||||
adjust4 |= (adjust4 << 8);
|
||||
adjust4 |= (adjust4 << 16);
|
||||
|
||||
for (i = ((subbands / 4) - 1); i >= 0; --i) {
|
||||
OI_UINT32 mask;
|
||||
OI_UINT32 n = bitneeds[i] + adjust4;
|
||||
mask = 0x7F7F7F7F + ((n & 0x40404040) >> 6);
|
||||
n &= mask;
|
||||
mask = 0x0F0F0F0F + ((n & 0x10101010) >> 4);
|
||||
n &= mask;
|
||||
mask = (((n + 0x0E0E0E0E) >> 4) | 0x1E1E1E1E);
|
||||
n &= mask;
|
||||
total += n;
|
||||
}
|
||||
|
||||
count = (total & 0xFFFF) + (total >> 16);
|
||||
count = (count & 0xFF) + (count >> 8);
|
||||
|
||||
chop >>= 1;
|
||||
if (count > bitpool) {
|
||||
bitadjust -= chop;
|
||||
} else {
|
||||
maxBitadjust = bitadjust;
|
||||
bitcount = count;
|
||||
bitadjust += chop;
|
||||
}
|
||||
}
|
||||
|
||||
*excess = bitpool - bitcount;
|
||||
|
||||
return maxBitadjust;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The bit allocator trys to avoid single bit allocations except as a last resort. So in the case
|
||||
* where a bitneed of 1 was passed over during the adsjustment phase 2 bits are now allocated.
|
||||
*/
|
||||
INLINE OI_INT allocAdjustedBits(OI_UINT8 *dest,
|
||||
OI_INT bits,
|
||||
OI_INT excess)
|
||||
{
|
||||
if (bits < 16) {
|
||||
if (bits > 1) {
|
||||
if (excess) {
|
||||
++bits;
|
||||
--excess;
|
||||
}
|
||||
} else if ((bits == 1) && (excess > 1)) {
|
||||
bits = 2;
|
||||
excess -= 2;
|
||||
} else {
|
||||
bits = 0;
|
||||
}
|
||||
} else {
|
||||
bits = 16;
|
||||
}
|
||||
*dest = (OI_UINT8)bits;
|
||||
return excess;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Excess bits not allocated by allocaAdjustedBits are allocated round-robin.
|
||||
*/
|
||||
INLINE OI_INT allocExcessBits(OI_UINT8 *dest,
|
||||
OI_INT excess)
|
||||
{
|
||||
if (*dest < 16) {
|
||||
*dest += 1;
|
||||
return excess - 1;
|
||||
} else {
|
||||
return excess;
|
||||
}
|
||||
}
|
||||
|
||||
void oneChannelBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common,
|
||||
BITNEED_UNION1 *bitneeds,
|
||||
OI_UINT ch,
|
||||
OI_UINT bitcount)
|
||||
{
|
||||
const OI_UINT8 nrof_subbands = common->frameInfo.nrof_subbands;
|
||||
OI_UINT excess;
|
||||
OI_UINT sb;
|
||||
OI_INT bitadjust;
|
||||
OI_UINT8 RESTRICT *allocBits;
|
||||
|
||||
|
||||
{
|
||||
OI_UINT ex;
|
||||
bitadjust = adjustToFitBitpool(common->frameInfo.bitpool, bitneeds->uint32, nrof_subbands, bitcount, &ex);
|
||||
/* We want the compiler to put excess into a register */
|
||||
excess = ex;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate adjusted bits
|
||||
*/
|
||||
allocBits = &common->bits.uint8[ch ? nrof_subbands : 0];
|
||||
|
||||
sb = 0;
|
||||
while (sb < nrof_subbands) {
|
||||
excess = allocAdjustedBits(&allocBits[sb], bitneeds->uint8[sb] + bitadjust, excess);
|
||||
++sb;
|
||||
}
|
||||
sb = 0;
|
||||
while (excess) {
|
||||
excess = allocExcessBits(&allocBits[sb], excess);
|
||||
++sb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void monoBitAllocation(OI_CODEC_SBC_COMMON_CONTEXT *common)
|
||||
{
|
||||
BITNEED_UNION1 bitneeds;
|
||||
OI_UINT bitcount;
|
||||
OI_UINT bitpoolPreference = 0;
|
||||
|
||||
bitcount = computeBitneed(common, bitneeds.uint8, 0, &bitpoolPreference);
|
||||
|
||||
oneChannelBitAllocation(common, &bitneeds, 0, bitcount);
|
||||
}
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
@@ -0,0 +1,92 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/**
|
||||
@file
|
||||
Functions for manipulating input bitstreams.
|
||||
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**
|
||||
@addtogroup codec_internal
|
||||
@{
|
||||
*/
|
||||
|
||||
#include "oi_stddefs.h"
|
||||
#include "oi_bitstream.h"
|
||||
#include "oi_assert.h"
|
||||
|
||||
PRIVATE void OI_BITSTREAM_ReadInit(OI_BITSTREAM *bs,
|
||||
const OI_BYTE *buffer)
|
||||
{
|
||||
bs->value = ((OI_INT32)buffer[0] << 16) | ((OI_INT32)buffer[1] << 8) | (buffer[2]);
|
||||
bs->ptr.r = buffer + 3;
|
||||
bs->bitPtr = 8;
|
||||
}
|
||||
|
||||
PRIVATE OI_UINT32 OI_BITSTREAM_ReadUINT(OI_BITSTREAM *bs, OI_UINT bits)
|
||||
{
|
||||
OI_UINT32 result;
|
||||
|
||||
OI_BITSTREAM_READUINT(result, bits, bs->ptr.r, bs->value, bs->bitPtr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PRIVATE OI_UINT8 OI_BITSTREAM_ReadUINT4Aligned(OI_BITSTREAM *bs)
|
||||
{
|
||||
OI_UINT32 result;
|
||||
|
||||
OI_ASSERT(bs->bitPtr < 16);
|
||||
OI_ASSERT(bs->bitPtr % 4 == 0);
|
||||
|
||||
if (bs->bitPtr == 8) {
|
||||
result = bs->value << 8;
|
||||
bs->bitPtr = 12;
|
||||
} else {
|
||||
result = bs->value << 12;
|
||||
bs->value = (bs->value << 8) | *bs->ptr.r++;
|
||||
bs->bitPtr = 8;
|
||||
}
|
||||
result >>= 28;
|
||||
OI_ASSERT(result < (1u << 4));
|
||||
return (OI_UINT8)result;
|
||||
}
|
||||
|
||||
PRIVATE OI_UINT8 OI_BITSTREAM_ReadUINT8Aligned(OI_BITSTREAM *bs)
|
||||
{
|
||||
OI_UINT32 result;
|
||||
OI_ASSERT(bs->bitPtr == 8);
|
||||
|
||||
result = bs->value >> 16;
|
||||
bs->value = (bs->value << 8) | *bs->ptr.r++;
|
||||
|
||||
return (OI_UINT8)result;
|
||||
}
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2006 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/**
|
||||
@file
|
||||
This file exposes OINA-specific interfaces to decoder functions.
|
||||
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**
|
||||
@addtogroup codec_internal
|
||||
@{
|
||||
*/
|
||||
|
||||
|
||||
#include <oi_codec_sbc_private.h>
|
||||
|
||||
OI_STATUS OI_CODEC_SBC_DecoderConfigureRaw(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
OI_BOOL enhanced,
|
||||
OI_UINT8 frequency,
|
||||
OI_UINT8 mode,
|
||||
OI_UINT8 subbands,
|
||||
OI_UINT8 blocks,
|
||||
OI_UINT8 alloc,
|
||||
OI_UINT8 maxBitpool)
|
||||
{
|
||||
if (frequency > SBC_FREQ_48000) {
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (enhanced) {
|
||||
#ifdef SBC_ENHANCED
|
||||
if (subbands != SBC_SUBBANDS_8) {
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
#else
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (mode > SBC_JOINT_STEREO) {
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (subbands > SBC_SUBBANDS_8) {
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (blocks > SBC_BLOCKS_16) {
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (alloc > SBC_SNR) {
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
#ifdef SBC_ENHANCED
|
||||
context->common.frameInfo.enhanced = enhanced;
|
||||
#else
|
||||
context->common.frameInfo.enhanced = FALSE;
|
||||
#endif
|
||||
context->common.frameInfo.freqIndex = frequency;
|
||||
context->common.frameInfo.mode = mode;
|
||||
context->common.frameInfo.subbands = subbands;
|
||||
context->common.frameInfo.blocks = blocks;
|
||||
context->common.frameInfo.alloc = alloc;
|
||||
context->common.frameInfo.bitpool = maxBitpool;
|
||||
|
||||
OI_SBC_ExpandFrameFields(&context->common.frameInfo);
|
||||
|
||||
if (context->common.frameInfo.nrof_channels >= context->common.pcmStride) {
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
return OI_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
OI_STATUS OI_CODEC_SBC_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
OI_UINT8 bitpool,
|
||||
const OI_BYTE **frameData,
|
||||
OI_UINT32 *frameBytes,
|
||||
OI_INT16 *pcmData,
|
||||
OI_UINT32 *pcmBytes)
|
||||
{
|
||||
return internal_DecodeRaw(context,
|
||||
bitpool,
|
||||
frameData,
|
||||
frameBytes,
|
||||
pcmData,
|
||||
pcmBytes);
|
||||
}
|
||||
|
||||
OI_STATUS OI_CODEC_SBC_DecoderLimit(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
OI_BOOL enhanced,
|
||||
OI_UINT8 subbands)
|
||||
{
|
||||
if (enhanced)
|
||||
{
|
||||
#ifdef SBC_ENHANCED
|
||||
context->enhancedEnabled = TRUE;
|
||||
#else
|
||||
context->enhancedEnabled = FALSE;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
context->enhancedEnabled = FALSE;
|
||||
}
|
||||
context->restrictSubbands = subbands;
|
||||
context->limitFrameFormat = TRUE;
|
||||
return OI_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
@@ -0,0 +1,272 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/**
|
||||
@file
|
||||
This file drives SBC decoding.
|
||||
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**
|
||||
@addtogroup codec_internal
|
||||
@{
|
||||
*/
|
||||
|
||||
#include "oi_codec_sbc_private.h"
|
||||
#include "oi_bitstream.h"
|
||||
#include <stdio.h>
|
||||
|
||||
const OI_CHAR * OI_Codec_Copyright = "Copyright 2002-2007 Open Interface North America, Inc. All rights reserved";
|
||||
|
||||
INLINE OI_STATUS internal_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
OI_UINT32 *decoderData,
|
||||
OI_UINT32 decoderDataBytes,
|
||||
OI_BYTE maxChannels,
|
||||
OI_BYTE pcmStride,
|
||||
OI_BOOL enhanced)
|
||||
{
|
||||
OI_UINT i;
|
||||
OI_STATUS status;
|
||||
|
||||
for (i = 0; i < sizeof(*context); i++) {
|
||||
((char *)context)[i] = 0;
|
||||
}
|
||||
|
||||
#ifdef SBC_ENHANCED
|
||||
context->enhancedEnabled = enhanced ? TRUE : FALSE;
|
||||
#else
|
||||
context->enhancedEnabled = FALSE;
|
||||
if (enhanced){
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
#endif
|
||||
|
||||
status = OI_CODEC_SBC_Alloc(&context->common, decoderData, decoderDataBytes, maxChannels, pcmStride);
|
||||
|
||||
if (!OI_SUCCESS(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
context->common.codecInfo = OI_Codec_Copyright;
|
||||
context->common.maxBitneed = 0;
|
||||
context->limitFrameFormat = FALSE;
|
||||
OI_SBC_ExpandFrameFields(&context->common.frameInfo);
|
||||
|
||||
/*PLATFORM_DECODER_RESET(context);*/
|
||||
|
||||
return OI_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Read the SBC header up to but not including the joint stereo mask. The syncword has already been
|
||||
* examined, and the enhanced mode flag set, by FindSyncword.
|
||||
*/
|
||||
|
||||
/* BK4BTSTACK_CHANGE START */
|
||||
INLINE void OI_SBC_ReadHeader_mSBC(OI_CODEC_SBC_COMMON_CONTEXT *common, const OI_BYTE *data){
|
||||
OI_CODEC_SBC_FRAME_INFO *frame = &common->frameInfo;
|
||||
|
||||
OI_ASSERT(data[0] == OI_mSBC_SYNCWORD);
|
||||
|
||||
/* Avoid filling out all these strucutures if we already remember the values
|
||||
* from last time. Just in case we get a stream corresponding to data[1] ==
|
||||
* 0, DecoderReset is responsible for ensuring the lookup table entries have
|
||||
* already been populated
|
||||
*/
|
||||
frame->reserved_for_future_use[0] = data[1];
|
||||
frame->reserved_for_future_use[1] = data[2];
|
||||
|
||||
frame->freqIndex = 0;
|
||||
frame->frequency = 16000;
|
||||
|
||||
frame->blocks = 4; // ?
|
||||
frame->nrof_blocks = 15;
|
||||
|
||||
frame->mode = 0;
|
||||
frame->nrof_channels = 1;
|
||||
|
||||
frame->alloc = SBC_LOUDNESS;
|
||||
|
||||
frame->subbands = 1;
|
||||
frame->nrof_subbands = 8;
|
||||
|
||||
frame->bitpool = 26;
|
||||
|
||||
frame->crc = data[3];
|
||||
|
||||
frame->cachedInfo = 0;
|
||||
}
|
||||
|
||||
/* BK4BTSTACK_CHANGE END */
|
||||
|
||||
|
||||
INLINE void OI_SBC_ReadHeader(OI_CODEC_SBC_COMMON_CONTEXT *common, const OI_BYTE *data)
|
||||
{
|
||||
OI_CODEC_SBC_FRAME_INFO *frame = &common->frameInfo;
|
||||
OI_UINT8 d1;
|
||||
|
||||
OI_ASSERT(data[0] == OI_SBC_SYNCWORD || data[0] == OI_SBC_ENHANCED_SYNCWORD);
|
||||
|
||||
/* Avoid filling out all these strucutures if we already remember the values
|
||||
* from last time. Just in case we get a stream corresponding to data[1] ==
|
||||
* 0, DecoderReset is responsible for ensuring the lookup table entries have
|
||||
* already been populated
|
||||
*/
|
||||
d1 = data[1];
|
||||
if (d1 != frame->cachedInfo) {
|
||||
|
||||
frame->freqIndex = (d1 & (BIT7 | BIT6)) >> 6;
|
||||
frame->frequency = freq_values[frame->freqIndex];
|
||||
|
||||
frame->blocks = (d1 & (BIT5 | BIT4)) >> 4;
|
||||
frame->nrof_blocks = block_values[frame->blocks];
|
||||
|
||||
frame->mode = (d1 & (BIT3 | BIT2)) >> 2;
|
||||
frame->nrof_channels = channel_values[frame->mode];
|
||||
|
||||
frame->alloc = (d1 & BIT1) >> 1;
|
||||
|
||||
frame->subbands = (d1 & BIT0);
|
||||
frame->nrof_subbands = band_values[frame->subbands];
|
||||
|
||||
frame->cachedInfo = d1;
|
||||
}
|
||||
/*
|
||||
* For decode, the bit allocator needs to know the bitpool value
|
||||
*/
|
||||
frame->bitpool = data[2];
|
||||
frame->crc = data[3];
|
||||
}
|
||||
|
||||
|
||||
#define LOW(x) ((x)& 0xf)
|
||||
#define HIGH(x) ((x) >> 4)
|
||||
|
||||
/*
|
||||
* Read scalefactor values and prepare the bitstream for OI_SBC_ReadSamples
|
||||
*/
|
||||
PRIVATE void OI_SBC_ReadScalefactors(OI_CODEC_SBC_COMMON_CONTEXT *common,
|
||||
const OI_BYTE *b,
|
||||
OI_BITSTREAM *bs)
|
||||
{
|
||||
OI_UINT i = common->frameInfo.nrof_subbands * common->frameInfo.nrof_channels;
|
||||
OI_INT8 *scale_factor = common->scale_factor;
|
||||
OI_UINT f;
|
||||
|
||||
if ((common->frameInfo.nrof_subbands == 8) || (common->frameInfo.mode != SBC_JOINT_STEREO)) {
|
||||
if (common->frameInfo.mode == SBC_JOINT_STEREO) {
|
||||
common->frameInfo.join = *b++;
|
||||
} else {
|
||||
common->frameInfo.join = 0;
|
||||
}
|
||||
i /= 2;
|
||||
do {
|
||||
*scale_factor++ = HIGH(f = *b++);
|
||||
*scale_factor++ = LOW(f);
|
||||
} while (--i);
|
||||
/*
|
||||
* In this case we know that the scale factors end on a byte boundary so all we need to do
|
||||
* is initialize the bitstream.
|
||||
*/
|
||||
OI_BITSTREAM_ReadInit(bs, b);
|
||||
} else {
|
||||
OI_ASSERT(common->frameInfo.nrof_subbands == 4 && common->frameInfo.mode == SBC_JOINT_STEREO);
|
||||
common->frameInfo.join = HIGH(f = *b++);
|
||||
i = (i - 1) / 2;
|
||||
do {
|
||||
*scale_factor++ = LOW(f);
|
||||
*scale_factor++ = HIGH(f = *b++);
|
||||
} while (--i);
|
||||
*scale_factor++ = LOW(f);
|
||||
/*
|
||||
* In 4-subband joint stereo mode, the joint stereo information ends on a half-byte
|
||||
* boundary, so it's necessary to use the bitstream abstraction to read it, since
|
||||
* OI_SBC_ReadSamples will need to pick up in mid-byte.
|
||||
*/
|
||||
OI_BITSTREAM_ReadInit(bs, b);
|
||||
*scale_factor++ = OI_BITSTREAM_ReadUINT4Aligned(bs);
|
||||
}
|
||||
}
|
||||
|
||||
/** Read quantized subband samples from the input bitstream and expand them. */
|
||||
PRIVATE void OI_SBC_ReadSamples(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs)
|
||||
{
|
||||
OI_CODEC_SBC_COMMON_CONTEXT *common = &context->common;
|
||||
OI_UINT nrof_blocks = common->frameInfo.nrof_blocks;
|
||||
OI_INT32 * RESTRICT s = common->subdata;
|
||||
OI_UINT8 *ptr = global_bs->ptr.w;
|
||||
OI_UINT32 value = global_bs->value;
|
||||
OI_UINT bitPtr = global_bs->bitPtr;
|
||||
|
||||
const OI_UINT iter_count = common->frameInfo.nrof_channels * common->frameInfo.nrof_subbands / 4;
|
||||
do {
|
||||
OI_UINT i;
|
||||
for (i = 0; i < iter_count; ++i) {
|
||||
OI_UINT32 sf_by4 = ((OI_UINT32*)common->scale_factor)[i];
|
||||
OI_UINT32 bits_by4 = common->bits.uint32[i];
|
||||
OI_UINT n;
|
||||
for (n = 0; n < 4; ++n) {
|
||||
OI_INT32 dequant;
|
||||
OI_UINT bits;
|
||||
OI_INT sf;
|
||||
|
||||
if (OI_CPU_BYTE_ORDER == OI_LITTLE_ENDIAN_BYTE_ORDER) {
|
||||
bits = bits_by4 & 0xFF;
|
||||
bits_by4 >>= 8;
|
||||
sf = sf_by4 & 0xFF;
|
||||
sf_by4 >>= 8;
|
||||
} else {
|
||||
bits = (bits_by4 >> 24) & 0xFF;
|
||||
bits_by4 <<= 8;
|
||||
sf = (sf_by4 >> 24) & 0xFF;
|
||||
sf_by4 <<= 8;
|
||||
}
|
||||
|
||||
if (bits) {
|
||||
OI_UINT32 raw;
|
||||
// return raw == audio sample (uint16)
|
||||
// bits == number of bits to read from stream
|
||||
// ptr == position in stream
|
||||
// value == 32bit value
|
||||
// bitPtr offset in 32bit value
|
||||
OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr);
|
||||
// dequant == sb_sample (int32)
|
||||
dequant = OI_SBC_Dequant(raw, sf, bits);
|
||||
} else {
|
||||
dequant = 0;
|
||||
}
|
||||
*s++ = dequant;
|
||||
}
|
||||
}
|
||||
} while (--nrof_blocks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
@@ -0,0 +1,498 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2006 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/** @file
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**@addtogroup codec_internal */
|
||||
/**@{*/
|
||||
|
||||
#include "oi_codec_sbc_private.h"
|
||||
#include "oi_bitstream.h"
|
||||
|
||||
#define SPECIALIZE_READ_SAMPLES_JOINT
|
||||
|
||||
/**
|
||||
* Scans through a buffer looking for a codec syncword. If the decoder has been
|
||||
* set for enhanced operation using OI_CODEC_SBC_DecoderReset(), it will search
|
||||
* for both a standard and an enhanced syncword.
|
||||
*/
|
||||
PRIVATE OI_STATUS FindSyncword(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
const OI_BYTE **frameData,
|
||||
OI_UINT32 *frameBytes);
|
||||
PRIVATE OI_STATUS FindSyncword(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
const OI_BYTE **frameData,
|
||||
OI_UINT32 *frameBytes)
|
||||
{
|
||||
#ifdef SBC_ENHANCED
|
||||
OI_BYTE search1 = OI_SBC_SYNCWORD;
|
||||
OI_BYTE search2 = OI_SBC_ENHANCED_SYNCWORD;
|
||||
#endif // SBC_ENHANCED
|
||||
|
||||
if (*frameBytes == 0) {
|
||||
return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA;
|
||||
}
|
||||
|
||||
#ifdef SBC_ENHANCED
|
||||
if (context->limitFrameFormat && context->enhancedEnabled){
|
||||
/* If the context is restricted, only search for specified SYNCWORD */
|
||||
search1 = search2;
|
||||
} else if (context->enhancedEnabled == FALSE) {
|
||||
/* If enhanced is not enabled, only search for classic SBC SYNCWORD*/
|
||||
search2 = search1;
|
||||
}
|
||||
while (*frameBytes && (**frameData != search1) && (**frameData != search2)) {
|
||||
(*frameBytes)--;
|
||||
(*frameData)++;
|
||||
}
|
||||
if (*frameBytes) {
|
||||
/* Syncword found, *frameData points to it, and *frameBytes correctly
|
||||
* reflects the number of bytes available to read, including the
|
||||
* syncword. */
|
||||
context->common.frameInfo.enhanced = (**frameData == OI_SBC_ENHANCED_SYNCWORD);
|
||||
return OI_OK;
|
||||
} else {
|
||||
/* No syncword was found anywhere in the provided input data.
|
||||
* *frameData points past the end of the original input, and
|
||||
* *frameBytes is 0. */
|
||||
return OI_CODEC_SBC_NO_SYNCWORD;
|
||||
}
|
||||
#else // SBC_ENHANCED
|
||||
/* BK4BTSTACK_CHANGE START */
|
||||
OI_UINT8 syncword = OI_SBC_SYNCWORD;
|
||||
if (context->common.frameInfo.mSBCEnabled){
|
||||
syncword = OI_mSBC_SYNCWORD;
|
||||
}
|
||||
/* BK4BTSTACK_CHANGE END */
|
||||
while (*frameBytes && (**frameData != syncword)) {
|
||||
(*frameBytes)--;
|
||||
(*frameData)++;
|
||||
}
|
||||
if (*frameBytes) {
|
||||
/* Syncword found, *frameData points to it, and *frameBytes correctly
|
||||
* reflects the number of bytes available to read, including the
|
||||
* syncword. */
|
||||
context->common.frameInfo.enhanced = FALSE;
|
||||
return OI_OK;
|
||||
} else {
|
||||
/* No syncword was found anywhere in the provided input data.
|
||||
* *frameData points past the end of the original input, and
|
||||
* *frameBytes is 0. */
|
||||
return OI_CODEC_SBC_NO_SYNCWORD;
|
||||
}
|
||||
#endif // SBC_ENHANCED
|
||||
}
|
||||
|
||||
|
||||
static OI_STATUS DecodeBody(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
const OI_BYTE *bodyData,
|
||||
OI_INT16 *pcmData,
|
||||
OI_UINT32 *pcmBytes,
|
||||
OI_BOOL allowPartial)
|
||||
{
|
||||
OI_BITSTREAM bs;
|
||||
OI_UINT frameSamples = context->common.frameInfo.nrof_blocks * context->common.frameInfo.nrof_subbands;
|
||||
OI_UINT decode_block_count;
|
||||
|
||||
/*
|
||||
* Based on the header data, make sure that there is enough room to write the output samples.
|
||||
*/
|
||||
if ((*pcmBytes < (sizeof(OI_INT16) * frameSamples * context->common.pcmStride)) && !allowPartial) {
|
||||
/* If we're not allowing partial decodes, we need room for the entire
|
||||
* codec frame */
|
||||
TRACE(("-OI_CODEC_SBC_Decode: OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA"));
|
||||
return OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA;
|
||||
} else if (*pcmBytes < (sizeof (OI_INT16) * context->common.frameInfo.nrof_subbands * context->common.pcmStride)) {
|
||||
/* Even if we're allowing partials, we can still only decode on a frame
|
||||
* boundary */
|
||||
return OI_CODEC_SBC_NOT_ENOUGH_AUDIO_DATA;
|
||||
}
|
||||
|
||||
if (context->bufferedBlocks == 0) {
|
||||
TRACE(("Reading scalefactors"));
|
||||
OI_SBC_ReadScalefactors(&context->common, bodyData, &bs);
|
||||
|
||||
TRACE(("Computing bit allocation"));
|
||||
OI_SBC_ComputeBitAllocation(&context->common);
|
||||
|
||||
TRACE(("Reading samples"));
|
||||
if (context->common.frameInfo.mode == SBC_JOINT_STEREO) {
|
||||
OI_SBC_ReadSamplesJoint(context, &bs);
|
||||
} else {
|
||||
OI_SBC_ReadSamples(context, &bs);
|
||||
}
|
||||
|
||||
context->bufferedBlocks = context->common.frameInfo.nrof_blocks;
|
||||
}
|
||||
|
||||
if (allowPartial) {
|
||||
decode_block_count = *pcmBytes / sizeof(OI_INT16) / context->common.pcmStride / context->common.frameInfo.nrof_subbands;
|
||||
|
||||
if (decode_block_count > context->bufferedBlocks) {
|
||||
decode_block_count = context->bufferedBlocks;
|
||||
}
|
||||
|
||||
} else {
|
||||
decode_block_count = context->common.frameInfo.nrof_blocks;
|
||||
}
|
||||
|
||||
TRACE(("Synthesizing frame"));
|
||||
{
|
||||
OI_UINT start_block = context->common.frameInfo.nrof_blocks - context->bufferedBlocks;
|
||||
OI_SBC_SynthFrame(context, pcmData, start_block, decode_block_count);
|
||||
}
|
||||
|
||||
OI_ASSERT(context->bufferedBlocks >= decode_block_count);
|
||||
context->bufferedBlocks -= decode_block_count;
|
||||
|
||||
frameSamples = decode_block_count * context->common.frameInfo.nrof_subbands;
|
||||
|
||||
/*
|
||||
* When decoding mono into a stride-2 array, copy pcm data to second channel
|
||||
*/
|
||||
if ((context->common.frameInfo.nrof_channels == 1) && (context->common.pcmStride == 2)) {
|
||||
OI_UINT i;
|
||||
for (i = 0; i < frameSamples; ++i) {
|
||||
pcmData[(2*i)+1] = pcmData[2*i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return number of pcm bytes generated by the decode operation.
|
||||
*/
|
||||
*pcmBytes = frameSamples * sizeof(OI_INT16) * context->common.pcmStride;
|
||||
|
||||
if (context->bufferedBlocks > 0) {
|
||||
return OI_CODEC_SBC_PARTIAL_DECODE;
|
||||
} else {
|
||||
return OI_OK;
|
||||
}
|
||||
}
|
||||
|
||||
PRIVATE OI_STATUS internal_DecodeRaw(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
OI_UINT8 bitpool,
|
||||
const OI_BYTE **frameData,
|
||||
OI_UINT32 *frameBytes,
|
||||
OI_INT16 *pcmData,
|
||||
OI_UINT32 *pcmBytes)
|
||||
{
|
||||
OI_STATUS status;
|
||||
OI_UINT bodyLen;
|
||||
|
||||
TRACE(("+OI_CODEC_SBC_DecodeRaw"));
|
||||
|
||||
if (context->bufferedBlocks == 0) {
|
||||
/*
|
||||
* The bitallocator needs to know the bitpool value.
|
||||
*/
|
||||
context->common.frameInfo.bitpool = bitpool;
|
||||
/*
|
||||
* Compute the frame length and check we have enough frame data to proceed
|
||||
*/
|
||||
bodyLen = OI_CODEC_SBC_CalculateFramelen(&context->common.frameInfo) - SBC_HEADER_LEN;
|
||||
if (*frameBytes < bodyLen) {
|
||||
TRACE(("-OI_CODEC_SBC_Decode: OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA"));
|
||||
return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA;
|
||||
}
|
||||
} else {
|
||||
bodyLen = 0;
|
||||
}
|
||||
/*
|
||||
* Decode the SBC data. Pass TRUE to DecodeBody to allow partial decoding of
|
||||
* tones.
|
||||
*/
|
||||
status = DecodeBody(context, *frameData, pcmData, pcmBytes, TRUE);
|
||||
if (OI_SUCCESS(status) || (status == OI_CODEC_SBC_PARTIAL_DECODE)) {
|
||||
*frameData += bodyLen;
|
||||
*frameBytes -= bodyLen;
|
||||
}
|
||||
TRACE(("-OI_CODEC_SBC_DecodeRaw: %d", status));
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
OI_STATUS OI_CODEC_SBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
OI_UINT32 *decoderData,
|
||||
OI_UINT32 decoderDataBytes,
|
||||
OI_UINT8 maxChannels,
|
||||
OI_UINT8 pcmStride,
|
||||
OI_BOOL enhanced)
|
||||
{
|
||||
return internal_DecoderReset(context, decoderData, decoderDataBytes, maxChannels, pcmStride, enhanced);
|
||||
}
|
||||
|
||||
/* BK4BTSTACK_CHANGE START */
|
||||
OI_STATUS OI_CODEC_mSBC_DecoderReset(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
OI_UINT32 *decoderData,
|
||||
OI_UINT32 decoderDataBytes)
|
||||
{
|
||||
OI_STATUS status = OI_CODEC_SBC_DecoderReset(context, decoderData, decoderDataBytes, 1, 1, FALSE);
|
||||
context->common.frameInfo.mSBCEnabled = TRUE;
|
||||
return status;
|
||||
}
|
||||
/* BK4BTSTACK_CHANGE END */
|
||||
|
||||
OI_STATUS OI_CODEC_SBC_DecodeFrame(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
const OI_BYTE **frameData,
|
||||
OI_UINT32 *frameBytes,
|
||||
OI_INT16 *pcmData,
|
||||
OI_UINT32 *pcmBytes)
|
||||
{
|
||||
OI_STATUS status;
|
||||
OI_UINT framelen;
|
||||
OI_UINT8 crc;
|
||||
|
||||
TRACE(("+OI_CODEC_SBC_DecodeFrame"));
|
||||
|
||||
TRACE(("Finding syncword"));
|
||||
status = FindSyncword(context, frameData, frameBytes);
|
||||
if (!OI_SUCCESS(status)) {
|
||||
return status;
|
||||
}
|
||||
/* Make sure enough data remains to read the header. */
|
||||
if (*frameBytes < SBC_HEADER_LEN) {
|
||||
TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA"));
|
||||
return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA;
|
||||
}
|
||||
|
||||
TRACE(("Reading Header"));
|
||||
if (context->common.frameInfo.mSBCEnabled){
|
||||
OI_SBC_ReadHeader_mSBC(&context->common, *frameData);
|
||||
} else {
|
||||
OI_SBC_ReadHeader(&context->common, *frameData);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some implementations load the decoder into RAM and use overlays for 4 vs 8 subbands. We need
|
||||
* to ensure that the SBC parameters for this frame are compatible with the restrictions imposed
|
||||
* by the loaded overlays.
|
||||
*/
|
||||
if (context->limitFrameFormat && (context->common.frameInfo.subbands != context->restrictSubbands)) {
|
||||
ERROR(("SBC parameters incompatible with loaded overlay"));
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
TRACE(("Frame: "));
|
||||
|
||||
if (context->common.frameInfo.nrof_channels > context->common.maxChannels) {
|
||||
ERROR(("SBC parameters incompatible with number of channels specified during reset"));
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if ((context->common.pcmStride < 1) || (context->common.pcmStride > 2)) {
|
||||
ERROR(("PCM stride not set correctly during reset"));
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point a header has been read. However, it's possible that we found a false syncword,
|
||||
* so the header data might be invalid. Make sure we have enough bytes to read in the
|
||||
* CRC-protected header, but don't require we have the whole frame. That way, if it turns out
|
||||
* that we're acting on bogus header data, we don't stall the decoding process by waiting for
|
||||
* data that we don't actually need.
|
||||
*/
|
||||
framelen = OI_CODEC_SBC_CalculateFramelen(&context->common.frameInfo);
|
||||
if (*frameBytes < framelen) {
|
||||
TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA"));
|
||||
return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA;
|
||||
}
|
||||
|
||||
TRACE(("Calculating checksum"));
|
||||
if (context->common.frameInfo.mSBCEnabled){
|
||||
crc = OI_SBC_CalculateChecksum_mSBC(&context->common.frameInfo, *frameData);
|
||||
} else {
|
||||
crc = OI_SBC_CalculateChecksum(&context->common.frameInfo, *frameData);
|
||||
}
|
||||
|
||||
if (crc != context->common.frameInfo.crc) {
|
||||
TRACE(("CRC Mismatch: calc=%02x read=%02x\n", crc, context->common.frameInfo.crc));
|
||||
TRACE(("-OI_CODEC_SBC_DecodeFrame: OI_CODEC_SBC_CHECKSUM_MISMATCH"));
|
||||
return OI_CODEC_SBC_CHECKSUM_MISMATCH;
|
||||
}
|
||||
|
||||
#ifdef OI_DEBUG
|
||||
/*
|
||||
* Make sure the bitpool values are sane.
|
||||
*/
|
||||
if ((context->common.frameInfo.bitpool < SBC_MIN_BITPOOL) && !context->common.frameInfo.enhanced) {
|
||||
ERROR(("Bitpool too small: %d (must be >= 2)", context->common.frameInfo.bitpool));
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
if (context->common.frameInfo.bitpool > OI_SBC_MaxBitpool(&context->common.frameInfo)) {
|
||||
ERROR(("Bitpool too large: %d (must be <= %ld)", context->common.frameInfo.bitpool, OI_SBC_MaxBitpool(&context->common.frameInfo)));
|
||||
return OI_STATUS_INVALID_PARAMETERS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Now decode the SBC data. Partial decode is not yet implemented for an SBC
|
||||
* stream, so pass FALSE to decode body to have it enforce the old rule that
|
||||
* you have to decode a whole packet at a time.
|
||||
*/
|
||||
status = DecodeBody(context, *frameData + SBC_HEADER_LEN, pcmData, pcmBytes, FALSE);
|
||||
if (OI_SUCCESS(status)) {
|
||||
*frameData += framelen;
|
||||
*frameBytes -= framelen;
|
||||
}
|
||||
TRACE(("-OI_CODEC_SBC_DecodeFrame: %d", status));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
OI_STATUS OI_CODEC_SBC_SkipFrame(OI_CODEC_SBC_DECODER_CONTEXT *context,
|
||||
const OI_BYTE **frameData,
|
||||
OI_UINT32 *frameBytes)
|
||||
{
|
||||
OI_STATUS status;
|
||||
OI_UINT framelen;
|
||||
OI_UINT headerlen;
|
||||
OI_UINT8 crc;
|
||||
|
||||
status = FindSyncword(context, frameData, frameBytes);
|
||||
if (!OI_SUCCESS(status)) {
|
||||
return status;
|
||||
}
|
||||
if (*frameBytes < SBC_HEADER_LEN) {
|
||||
return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA;
|
||||
}
|
||||
OI_SBC_ReadHeader(&context->common, *frameData);
|
||||
framelen = OI_SBC_CalculateFrameAndHeaderlen(&context->common.frameInfo, &headerlen);
|
||||
if (*frameBytes < headerlen) {
|
||||
return OI_CODEC_SBC_NOT_ENOUGH_HEADER_DATA;
|
||||
}
|
||||
crc = OI_SBC_CalculateChecksum(&context->common.frameInfo, *frameData);
|
||||
if (crc != context->common.frameInfo.crc) {
|
||||
return OI_CODEC_SBC_CHECKSUM_MISMATCH;
|
||||
}
|
||||
if (*frameBytes < framelen) {
|
||||
return OI_CODEC_SBC_NOT_ENOUGH_BODY_DATA;
|
||||
}
|
||||
context->bufferedBlocks = 0;
|
||||
*frameData += framelen;
|
||||
*frameBytes -= framelen;
|
||||
return OI_OK;
|
||||
}
|
||||
|
||||
OI_UINT8 OI_CODEC_SBC_FrameCount(OI_BYTE *frameData,
|
||||
OI_UINT32 frameBytes)
|
||||
{
|
||||
OI_UINT8 mode;
|
||||
OI_UINT8 blocks;
|
||||
OI_UINT8 subbands;
|
||||
OI_UINT8 frameCount = 0;
|
||||
OI_UINT frameLen;
|
||||
|
||||
while (frameBytes){
|
||||
while (frameBytes && ((frameData[0] & 0xFE) != 0x9C)){
|
||||
frameData++;
|
||||
frameBytes--;
|
||||
}
|
||||
|
||||
if (frameBytes < SBC_HEADER_LEN) {
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
/* Extract and translate required fields from Header */
|
||||
subbands = mode = blocks = frameData[1];;
|
||||
mode = (mode & (BIT3 | BIT2)) >> 2;
|
||||
blocks = block_values[(blocks & (BIT5 | BIT4)) >> 4];
|
||||
subbands = band_values[(subbands & BIT0)];
|
||||
|
||||
/* Inline logic to avoid corrupting context */
|
||||
frameLen = blocks * frameData[2];
|
||||
switch (mode){
|
||||
case SBC_JOINT_STEREO:
|
||||
frameLen += subbands + (8 * subbands);
|
||||
break;
|
||||
|
||||
case SBC_DUAL_CHANNEL:
|
||||
frameLen *= 2;
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
if (mode == SBC_MONO){
|
||||
frameLen += 4*subbands;
|
||||
} else {
|
||||
frameLen += 8*subbands;
|
||||
}
|
||||
}
|
||||
|
||||
frameCount++;
|
||||
frameLen = SBC_HEADER_LEN + ((frameLen + 7) / 8);
|
||||
if (frameBytes > frameLen){
|
||||
frameBytes -= frameLen;
|
||||
frameData += frameLen;
|
||||
} else {
|
||||
frameBytes = 0;
|
||||
}
|
||||
}
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
/** Read quantized subband samples from the input bitstream and expand them. */
|
||||
|
||||
#ifdef SPECIALIZE_READ_SAMPLES_JOINT
|
||||
|
||||
PRIVATE void OI_SBC_ReadSamplesJoint4(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs);
|
||||
PRIVATE void OI_SBC_ReadSamplesJoint4(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs)
|
||||
{
|
||||
#define NROF_SUBBANDS 4
|
||||
#include "readsamplesjoint.inc"
|
||||
#undef NROF_SUBBANDS
|
||||
}
|
||||
|
||||
PRIVATE void OI_SBC_ReadSamplesJoint8(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs);
|
||||
PRIVATE void OI_SBC_ReadSamplesJoint8(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs)
|
||||
{
|
||||
#define NROF_SUBBANDS 8
|
||||
#include "readsamplesjoint.inc"
|
||||
#undef NROF_SUBBANDS
|
||||
}
|
||||
|
||||
typedef void (*READ_SAMPLES)(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs);
|
||||
|
||||
static const READ_SAMPLES SpecializedReadSamples[] = {
|
||||
OI_SBC_ReadSamplesJoint4,
|
||||
OI_SBC_ReadSamplesJoint8
|
||||
};
|
||||
|
||||
#endif /* SPECIALIZE_READ_SAMPLES_JOINT */
|
||||
|
||||
|
||||
PRIVATE void OI_SBC_ReadSamplesJoint(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_BITSTREAM *global_bs)
|
||||
{
|
||||
OI_CODEC_SBC_COMMON_CONTEXT *common = &context->common;
|
||||
OI_UINT nrof_subbands = common->frameInfo.nrof_subbands;
|
||||
#ifdef SPECIALIZE_READ_SAMPLES_JOINT
|
||||
OI_ASSERT((nrof_subbands >> 3u) <= 1u);
|
||||
SpecializedReadSamples[nrof_subbands >> 3](context, global_bs);
|
||||
#else
|
||||
|
||||
#define NROF_SUBBANDS nrof_subbands
|
||||
#include "readsamplesjoint.inc"
|
||||
#undef NROF_SUBBANDS
|
||||
#endif /* SPECIALIZE_READ_SAMPLES_JOINT */
|
||||
}
|
||||
|
||||
/**@}*/
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/**
|
||||
@file
|
||||
|
||||
Dequantizer for SBC decoder; reconstructs quantized representation of subband samples.
|
||||
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**
|
||||
@addtogroup codec_internal
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
This function is a fixed-point approximation of a modification of the following
|
||||
dequantization operation defined in the spec, as inferred from section 12.6.4:
|
||||
|
||||
@code
|
||||
dequant = 2^(scale_factor+1) * ((raw * 2.0 + 1.0) / ((2^bits) - 1) - 1)
|
||||
|
||||
2 <= bits <= 16
|
||||
0 <= raw < (2^bits)-1 (the -1 is because quantized values with all 1's are forbidden)
|
||||
|
||||
-65535 < dequant < 65535
|
||||
@endcode
|
||||
|
||||
The code below computes the dequantized value divided by a scaling constant
|
||||
equal to about 1.38. This constant is chosen to ensure that the entry in the
|
||||
dequant_long_scaled table for 16 bits is as accurate as possible, since it has
|
||||
the least relative precision available to it due to its small magnitude.
|
||||
|
||||
This routine outputs in Q16.15 format.
|
||||
|
||||
The helper array dequant_long is defined as follows:
|
||||
|
||||
@code
|
||||
dequant_long_long[bits] = round(2^31 * 1/((2^bits - 1) / 1.38...) for 2 <= bits <= 16
|
||||
@endcode
|
||||
|
||||
|
||||
Additionally, the table entries have the following property:
|
||||
|
||||
@code
|
||||
dequant_long_scaled[bits] <= 2^31 / ((2^bits - 1)) for 2 <= bits <= 16
|
||||
@endcode
|
||||
|
||||
Therefore
|
||||
|
||||
@code
|
||||
d = 2 * raw + 1 1 <= d <= 2^bits - 2
|
||||
|
||||
d' = d * dequant_long[bits]
|
||||
|
||||
d * dequant_long_scaled[bits] <= (2^bits - 2) * (2^31 / (2^bits - 1))
|
||||
d * dequant_long_scaled[bits] <= 2^31 * (2^bits - 2)/(2^bits - 1) < 2^31
|
||||
@endcode
|
||||
|
||||
Therefore, d' doesn't overflow a signed 32-bit value.
|
||||
|
||||
@code
|
||||
|
||||
d' =~ 2^31 * (raw * 2.0 + 1.0) / (2^bits - 1) / 1.38...
|
||||
|
||||
result = d' - 2^31/1.38... =~ 2^31 * ((raw * 2.0 + 1.0) / (2^bits - 1) - 1) / 1.38...
|
||||
|
||||
result is therefore a scaled approximation to dequant. It remains only to
|
||||
turn 2^31 into 2^(scale_factor+1). Since we're aiming for Q16.15 format,
|
||||
this is achieved by shifting right by (15-scale_factor):
|
||||
|
||||
(2^31 * x) >> (15-scale_factor) =~ 2^(31-15+scale_factor) * x = 2^15 * 2^(1+scale_factor) * x
|
||||
@endcode
|
||||
|
||||
*/
|
||||
|
||||
#include <oi_codec_sbc_private.h>
|
||||
|
||||
#ifndef SBC_DEQUANT_LONG_SCALED_OFFSET
|
||||
#define SBC_DEQUANT_LONG_SCALED_OFFSET 1555931970
|
||||
#endif
|
||||
|
||||
#ifndef SBC_DEQUANT_LONG_UNSCALED_OFFSET
|
||||
#define SBC_DEQUANT_LONG_UNSCALED_OFFSET 2147483648
|
||||
#endif
|
||||
|
||||
#ifndef SBC_DEQUANT_SCALING_FACTOR
|
||||
#define SBC_DEQUANT_SCALING_FACTOR 1.38019122262781f
|
||||
#endif
|
||||
|
||||
extern const OI_UINT32 dequant_long_scaled[17];
|
||||
extern const OI_UINT32 dequant_long_unscaled[17];
|
||||
|
||||
/** Scales x by y bits to the right, adding a rounding factor.
|
||||
*/
|
||||
#ifndef SCALE
|
||||
#define SCALE(x, y) (((x) + (1 <<((y)-1))) >> (y))
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_DEQUANTIZATION
|
||||
|
||||
#include <math.h>
|
||||
|
||||
INLINE float dequant_float(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits)
|
||||
{
|
||||
float result = (1 << (scale_factor+1)) * ((raw * 2.0f + 1.0f) / ((1 << bits) - 1.0f) - 1.0f);
|
||||
|
||||
result /= SBC_DEQUANT_SCALING_FACTOR;
|
||||
|
||||
/* Unless the encoder screwed up, all correct dequantized values should
|
||||
* satisfy this inequality. Non-compliant encoders which generate quantized
|
||||
* values with all 1-bits set can, theoretically, trigger this assert. This
|
||||
* is unlikely, however, and only an issue in debug mode.
|
||||
*/
|
||||
OI_ASSERT(fabs(result) < 32768 * 1.6);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
INLINE OI_INT32 OI_SBC_Dequant(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits)
|
||||
{
|
||||
OI_UINT32 d;
|
||||
OI_INT32 result;
|
||||
|
||||
OI_ASSERT(scale_factor <= 15);
|
||||
OI_ASSERT(bits <= 16);
|
||||
|
||||
if (bits <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
d = (raw * 2) + 1;
|
||||
d *= dequant_long_scaled[bits];
|
||||
result = d - SBC_DEQUANT_LONG_SCALED_OFFSET;
|
||||
|
||||
#ifdef DEBUG_DEQUANTIZATION
|
||||
{
|
||||
OI_INT32 integerized_float_result;
|
||||
float float_result;
|
||||
|
||||
float_result = dequant_float(raw, scale_factor, bits);
|
||||
integerized_float_result = (OI_INT32)floor(0.5f+float_result * (1 << 15));
|
||||
|
||||
/* This detects overflow */
|
||||
OI_ASSERT(((result >= 0) && (integerized_float_result >= 0)) ||
|
||||
((result <= 0) && (integerized_float_result <= 0)));
|
||||
}
|
||||
#endif
|
||||
return result >> (15 - scale_factor);
|
||||
}
|
||||
|
||||
/* This version of Dequant does not incorporate the scaling factor of 1.38. It
|
||||
* is intended for use with implementations of the filterbank which are
|
||||
* hard-coded into a DSP. Output is Q16.4 format, so that after joint stereo
|
||||
* processing (which leaves the most significant bit equal to the sign bit if
|
||||
* the encoder is conformant) the result will fit a 24 bit fixed point signed
|
||||
* value.*/
|
||||
|
||||
INLINE OI_INT32 OI_SBC_Dequant_Unscaled(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits);
|
||||
INLINE OI_INT32 OI_SBC_Dequant_Unscaled(OI_UINT32 raw, OI_UINT scale_factor, OI_UINT bits)
|
||||
{
|
||||
OI_UINT32 d;
|
||||
OI_INT32 result;
|
||||
|
||||
OI_ASSERT(scale_factor <= 15);
|
||||
OI_ASSERT(bits <= 16);
|
||||
|
||||
|
||||
if (bits <= 1) {
|
||||
return 0;
|
||||
}
|
||||
if (bits == 16) {
|
||||
result = (raw << 16) + raw - 0x7fff7fff;
|
||||
return SCALE(result, 24 - scale_factor);
|
||||
}
|
||||
|
||||
|
||||
d = (raw * 2) + 1;
|
||||
d *= dequant_long_unscaled[bits];
|
||||
result = d - 0x80000000;
|
||||
|
||||
return SCALE(result, 24 - scale_factor);
|
||||
}
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
@@ -0,0 +1,55 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/** @file
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**@addgroup codec_internal*/
|
||||
/**@{*/
|
||||
|
||||
#include "oi_codec_sbc_private.h"
|
||||
|
||||
const OI_CHAR* const OI_CODEC_SBC_FreqText[] = { "SBC_FREQ_16000", "SBC_FREQ_32000", "SBC_FREQ_44100", "SBC_FREQ_48000" };
|
||||
const OI_CHAR* const OI_CODEC_SBC_ModeText[] = { "SBC_MONO", "SBC_DUAL_CHANNEL", "SBC_STEREO", "SBC_JOINT_STEREO" };
|
||||
const OI_CHAR* const OI_CODEC_SBC_SubbandsText[] = { "SBC_SUBBANDS_4", "SBC_SUBBANDS_8" };
|
||||
const OI_CHAR* const OI_CODEC_SBC_BlocksText[] = { "SBC_BLOCKS_4", "SBC_BLOCKS_8", "SBC_BLOCKS_12", "SBC_BLOCKS_16" };
|
||||
const OI_CHAR* const OI_CODEC_SBC_AllocText[] = { "SBC_LOUDNESS", "SBC_SNR" };
|
||||
|
||||
#ifdef OI_DEBUG
|
||||
#include <stdio.h>
|
||||
void OI_CODEC_SBC_DumpConfig(OI_CODEC_SBC_FRAME_INFO *frameInfo)
|
||||
{
|
||||
printf("SBC configuration\n");
|
||||
printf(" enhanced: %s\n", frameInfo->enhanced ? "TRUE" : "FALSE");
|
||||
printf(" frequency: %d\n", frameInfo->frequency);
|
||||
printf(" subbands: %d\n", frameInfo->nrof_subbands);
|
||||
printf(" blocks: %d\n", frameInfo->nrof_blocks);
|
||||
printf(" channels: %d\n", frameInfo->nrof_channels);
|
||||
printf(" mode: %s\n", OI_CODEC_SBC_ModeText[frameInfo->mode]);
|
||||
printf(" alloc: %s\n", OI_CODEC_SBC_AllocText[frameInfo->alloc]);
|
||||
printf(" bitpool: %d\n", frameInfo->bitpool);
|
||||
}
|
||||
#endif /* OI_DEBUG */
|
||||
|
||||
/**@}*/
|
||||
@@ -0,0 +1,278 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/**
|
||||
@file
|
||||
Checksum and header-related functions.
|
||||
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**
|
||||
@addtogroup codec_internal
|
||||
@{
|
||||
*/
|
||||
|
||||
#include "oi_codec_sbc_private.h"
|
||||
#include "oi_assert.h"
|
||||
|
||||
|
||||
/* asdasd */
|
||||
|
||||
#define USE_NIBBLEWISE_CRC
|
||||
|
||||
/* #define PRINT_SAMPLES */
|
||||
/* #define PRINT_SCALEFACTORS */
|
||||
/* #define DEBUG_CRC */
|
||||
|
||||
/*
|
||||
* CRC-8 table for X^8 + X^4 + X^3 + X^2 + 1; byte-wise lookup
|
||||
*/
|
||||
#ifdef USE_WIDE_CRC
|
||||
/* Save space if a char is 16 bits, such as on the C54x */
|
||||
const OI_BYTE crc8_wide[128] = {
|
||||
0x001d, 0x3a27, 0x7469, 0x4e53, 0xe8f5, 0xd2cf, 0x9c81, 0xa6bb, 0xcdd0, 0xf7ea, 0xb9a4, 0x839e, 0x2538, 0x1f02, 0x514c, 0x6b76, 0x879a, 0xbda0, 0xf3ee, 0xc9d4, 0x6f72, 0x5548, 0x1b06, 0x213c, 0x4a57, 0x706d, 0x3e23, 0x0419, 0xa2bf, 0x9885, 0xd6cb, 0xecf1, 0x130e, 0x2934, 0x677a, 0x5d40, 0xfbe6, 0xc1dc, 0x8f92, 0xb5a8, 0xdec3, 0xe4f9, 0xaab7, 0x908d, 0x362b, 0x0c11, 0x425f, 0x7865, 0x9489, 0xaeb3, 0xe0fd, 0xdac7, 0x7c61, 0x465b, 0x0815, 0x322f, 0x5944, 0x637e, 0x2d30, 0x170a, 0xb1ac, 0x8b96, 0xc5d8, 0xffe2, 0x263b, 0x1c01, 0x524f, 0x6875, 0xced3, 0xf4e9, 0xbaa7, 0x809d, 0xebf6, 0xd1cc, 0x9f82, 0xa5b8, 0x031e, 0x3924, 0x776a, 0x4d50, 0xa1bc, 0x9b86, 0xd5c8, 0xeff2, 0x4954, 0x736e, 0x3d20, 0x071a, 0x6c71, 0x564b, 0x1805, 0x223f, 0x8499, 0xbea3, 0xf0ed, 0xcad7, 0x3528, 0x0f12, 0x415c, 0x7b66, 0xddc0, 0xe7fa, 0xa9b4, 0x938e, 0xf8e5, 0xc2df, 0x8c91, 0xb6ab, 0x100d, 0x2a37, 0x6479, 0x5e43, 0xb2af, 0x8895, 0xc6db, 0xfce1, 0x5a47, 0x607d, 0x2e33, 0x1409, 0x7f62, 0x4558, 0x0b16, 0x312c, 0x978a, 0xadb0, 0xe3fe, 0xd9c4,
|
||||
};
|
||||
#elif defined(USE_NIBBLEWISE_CRC)
|
||||
const OI_BYTE crc8_narrow[16] = {
|
||||
0x00, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb
|
||||
};
|
||||
#else
|
||||
const OI_BYTE crc8_narrow[256] = {
|
||||
0x00, 0x1d, 0x3a, 0x27, 0x74, 0x69, 0x4e, 0x53, 0xe8, 0xf5, 0xd2, 0xcf, 0x9c, 0x81, 0xa6, 0xbb, 0xcd, 0xd0, 0xf7, 0xea, 0xb9, 0xa4, 0x83, 0x9e, 0x25, 0x38, 0x1f, 0x02, 0x51, 0x4c, 0x6b, 0x76, 0x87, 0x9a, 0xbd, 0xa0, 0xf3, 0xee, 0xc9, 0xd4, 0x6f, 0x72, 0x55, 0x48, 0x1b, 0x06, 0x21, 0x3c, 0x4a, 0x57, 0x70, 0x6d, 0x3e, 0x23, 0x04, 0x19, 0xa2, 0xbf, 0x98, 0x85, 0xd6, 0xcb, 0xec, 0xf1, 0x13, 0x0e, 0x29, 0x34, 0x67, 0x7a, 0x5d, 0x40, 0xfb, 0xe6, 0xc1, 0xdc, 0x8f, 0x92, 0xb5, 0xa8, 0xde, 0xc3, 0xe4, 0xf9, 0xaa, 0xb7, 0x90, 0x8d, 0x36, 0x2b, 0x0c, 0x11, 0x42, 0x5f, 0x78, 0x65, 0x94, 0x89, 0xae, 0xb3, 0xe0, 0xfd, 0xda, 0xc7, 0x7c, 0x61, 0x46, 0x5b, 0x08, 0x15, 0x32, 0x2f, 0x59, 0x44, 0x63, 0x7e, 0x2d, 0x30, 0x17, 0x0a, 0xb1, 0xac, 0x8b, 0x96, 0xc5, 0xd8, 0xff, 0xe2, 0x26, 0x3b, 0x1c, 0x01, 0x52, 0x4f, 0x68, 0x75, 0xce, 0xd3, 0xf4, 0xe9, 0xba, 0xa7, 0x80, 0x9d, 0xeb, 0xf6, 0xd1, 0xcc, 0x9f, 0x82, 0xa5, 0xb8, 0x03, 0x1e, 0x39, 0x24, 0x77, 0x6a, 0x4d, 0x50, 0xa1, 0xbc, 0x9b, 0x86, 0xd5, 0xc8, 0xef, 0xf2, 0x49, 0x54, 0x73, 0x6e, 0x3d, 0x20, 0x07, 0x1a, 0x6c, 0x71, 0x56, 0x4b, 0x18, 0x05, 0x22, 0x3f, 0x84, 0x99, 0xbe, 0xa3, 0xf0, 0xed, 0xca, 0xd7, 0x35, 0x28, 0x0f, 0x12, 0x41, 0x5c, 0x7b, 0x66, 0xdd, 0xc0, 0xe7, 0xfa, 0xa9, 0xb4, 0x93, 0x8e, 0xf8, 0xe5, 0xc2, 0xdf, 0x8c, 0x91, 0xb6, 0xab, 0x10, 0x0d, 0x2a, 0x37, 0x64, 0x79, 0x5e, 0x43, 0xb2, 0xaf, 0x88, 0x95, 0xc6, 0xdb, 0xfc, 0xe1, 0x5a, 0x47, 0x60, 0x7d, 0x2e, 0x33, 0x14, 0x09, 0x7f, 0x62, 0x45, 0x58, 0x0b, 0x16, 0x31, 0x2c, 0x97, 0x8a, 0xad, 0xb0, 0xe3, 0xfe, 0xd9, 0xc4
|
||||
};
|
||||
#endif
|
||||
|
||||
const OI_UINT32 dequant_long_scaled[17] = {
|
||||
0,
|
||||
0,
|
||||
0x1ee9e116, /* bits=2 0.24151243 1/3 * (1/1.38019122262781) (0x00000008)*/
|
||||
0x0d3fa99c, /* bits=3 0.10350533 1/7 * (1/1.38019122262781) (0x00000013)*/
|
||||
0x062ec69e, /* bits=4 0.04830249 1/15 * (1/1.38019122262781) (0x00000029)*/
|
||||
0x02fddbfa, /* bits=5 0.02337217 1/31 * (1/1.38019122262781) (0x00000055)*/
|
||||
0x0178d9f5, /* bits=6 0.01150059 1/63 * (1/1.38019122262781) (0x000000ad)*/
|
||||
0x00baf129, /* bits=7 0.00570502 1/127 * (1/1.38019122262781) (0x0000015e)*/
|
||||
0x005d1abe, /* bits=8 0.00284132 1/255 * (1/1.38019122262781) (0x000002bf)*/
|
||||
0x002e760d, /* bits=9 0.00141788 1/511 * (1/1.38019122262781) (0x00000582)*/
|
||||
0x00173536, /* bits=10 0.00070825 1/1023 * (1/1.38019122262781) (0x00000b07)*/
|
||||
0x000b9928, /* bits=11 0.00035395 1/2047 * (1/1.38019122262781) (0x00001612)*/
|
||||
0x0005cc37, /* bits=12 0.00017693 1/4095 * (1/1.38019122262781) (0x00002c27)*/
|
||||
0x0002e604, /* bits=13 0.00008846 1/8191 * (1/1.38019122262781) (0x00005852)*/
|
||||
0x000172fc, /* bits=14 0.00004422 1/16383 * (1/1.38019122262781) (0x0000b0a7)*/
|
||||
0x0000b97d, /* bits=15 0.00002211 1/32767 * (1/1.38019122262781) (0x00016150)*/
|
||||
0x00005cbe, /* bits=16 0.00001106 1/65535 * (1/1.38019122262781) (0x0002c2a5)*/
|
||||
};
|
||||
|
||||
|
||||
const OI_UINT32 dequant_long_unscaled[17] = {
|
||||
0,
|
||||
0,
|
||||
0x2aaaaaab, /* bits=2 0.33333333 1/3 (0x00000005)*/
|
||||
0x12492492, /* bits=3 0.14285714 1/7 (0x0000000e)*/
|
||||
0x08888889, /* bits=4 0.06666667 1/15 (0x0000001d)*/
|
||||
0x04210842, /* bits=5 0.03225806 1/31 (0x0000003e)*/
|
||||
0x02082082, /* bits=6 0.01587302 1/63 (0x0000007e)*/
|
||||
0x01020408, /* bits=7 0.00787402 1/127 (0x000000fe)*/
|
||||
0x00808081, /* bits=8 0.00392157 1/255 (0x000001fd)*/
|
||||
0x00402010, /* bits=9 0.00195695 1/511 (0x000003fe)*/
|
||||
0x00200802, /* bits=10 0.00097752 1/1023 (0x000007fe)*/
|
||||
0x00100200, /* bits=11 0.00048852 1/2047 (0x00000ffe)*/
|
||||
0x00080080, /* bits=12 0.00024420 1/4095 (0x00001ffe)*/
|
||||
0x00040020, /* bits=13 0.00012209 1/8191 (0x00003ffe)*/
|
||||
0x00020008, /* bits=14 0.00006104 1/16383 (0x00007ffe)*/
|
||||
0x00010002, /* bits=15 0.00003052 1/32767 (0x0000fffe)*/
|
||||
0x00008001, /* bits=16 0.00001526 1/65535 (0x0001fffc)*/
|
||||
};
|
||||
|
||||
#if defined(OI_DEBUG) || defined(PRINT_SAMPLES) || defined(PRINT_SCALEFACTORS)
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_WIDE_CRC
|
||||
INLINE OI_CHAR crc_iterate(OI_UINT8 oldcrc, OI_UINT8 next);
|
||||
INLINE OI_CHAR crc_iterate(OI_UINT8 oldcrc, OI_UINT8 next)
|
||||
{
|
||||
OI_UINT crc;
|
||||
OI_UINT idx;
|
||||
idx = oldcrc^next;
|
||||
crc = crc8_wide[idx >> 1];
|
||||
if (idx%2) {
|
||||
crc &= 0xff;
|
||||
} else {
|
||||
crc >>= 8;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
INLINE OI_CHAR crc_iterate_top4(OI_UINT8 oldcrc, OI_UINT8 next);
|
||||
INLINE OI_CHAR crc_iterate_top4(OI_UINT8 oldcrc, OI_UINT8 next)
|
||||
{
|
||||
OI_UINT crc;
|
||||
OI_UINT idx;
|
||||
idx = (oldcrc ^ next) >> 4;
|
||||
crc = crc8_wide[idx>>1];
|
||||
if (idx%2) {
|
||||
crc &= 0xff;
|
||||
} else {
|
||||
crc >>= 8;
|
||||
}
|
||||
|
||||
return (oldcrc << 4) ^ crc;
|
||||
}
|
||||
|
||||
#else // USE_WIDE_CRC
|
||||
|
||||
INLINE OI_UINT8 crc_iterate_top4(OI_UINT8 oldcrc, OI_UINT8 next);
|
||||
INLINE OI_UINT8 crc_iterate_top4(OI_UINT8 oldcrc, OI_UINT8 next)
|
||||
{
|
||||
return (oldcrc << 4) ^ crc8_narrow[(oldcrc^next) >> 4];
|
||||
}
|
||||
|
||||
#ifdef USE_NIBBLEWISE_CRC
|
||||
INLINE OI_UINT8 crc_iterate(OI_UINT8 crc, OI_UINT8 next);
|
||||
INLINE OI_UINT8 crc_iterate(OI_UINT8 crc, OI_UINT8 next)
|
||||
{
|
||||
crc = (crc << 4) ^ crc8_narrow[(crc^next) >> 4];
|
||||
crc = (crc << 4) ^ crc8_narrow[((crc>>4)^next)&0xf];
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
#else // USE_NIBBLEWISE_CRC
|
||||
INLINE OI_UINT8 crc_iterate(OI_UINT8 crc, OI_UINT8 next);
|
||||
INLINE OI_UINT8 crc_iterate(OI_UINT8 crc, OI_UINT8 next)
|
||||
{
|
||||
return crc8_narrow[crc^next];
|
||||
}
|
||||
|
||||
#endif // USE_NIBBLEWISE_CRC
|
||||
|
||||
#endif // USE_WIDE_CRC
|
||||
|
||||
PRIVATE OI_UINT8 OI_SBC_CalculateChecksum_mSBC(OI_CODEC_SBC_FRAME_INFO *frame, OI_BYTE const *data)
|
||||
{
|
||||
OI_UINT i;
|
||||
OI_UINT8 crc = 0x0f;
|
||||
/* Count is the number of whole bytes subject to CRC. Actually, it's one
|
||||
* more than this number, because data[3] is the CRC field itself, which is
|
||||
* explicitly skipped. Since crc_iterate (should be) inlined, it's cheaper
|
||||
* spacewise to include the check in the loop. This shouldn't be much of a
|
||||
* bottleneck routine in the first place. */
|
||||
|
||||
// 0 - syncword (skip)
|
||||
// 1 - reserved
|
||||
crc = crc_iterate(crc,frame->reserved_for_future_use[0]);
|
||||
// 2 - reserved
|
||||
crc = crc_iterate(crc,frame->reserved_for_future_use[1]);
|
||||
// 3 - crc (skip)
|
||||
// 4..7 - scale factors (8 x 4 bit = 4 byte)
|
||||
for (i = 0; i < 4; i++) {
|
||||
crc = crc_iterate(crc,data[4+i]);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
PRIVATE OI_UINT8 OI_SBC_CalculateChecksum(OI_CODEC_SBC_FRAME_INFO *frame, OI_BYTE const *data)
|
||||
{
|
||||
OI_UINT i;
|
||||
OI_UINT8 crc = 0x0f;
|
||||
/* Count is the number of whole bytes subject to CRC. Actually, it's one
|
||||
* more than this number, because data[3] is the CRC field itself, which is
|
||||
* explicitly skipped. Since crc_iterate (should be) inlined, it's cheaper
|
||||
* spacewise to include the check in the loop. This shouldn't be much of a
|
||||
* bottleneck routine in the first place. */
|
||||
|
||||
OI_UINT count = (frame->nrof_subbands * frame->nrof_channels / 2u) + 4;
|
||||
|
||||
if ((frame->mode == SBC_JOINT_STEREO) && (frame->nrof_subbands == 8)) {
|
||||
count++;
|
||||
}
|
||||
|
||||
for (i = 1; i < count; i++) {
|
||||
if (i != 3) {
|
||||
crc = crc_iterate(crc,data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((frame->mode == SBC_JOINT_STEREO) && (frame->nrof_subbands == 4)) {
|
||||
crc = crc_iterate_top4(crc, data[i]);
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
void OI_SBC_ExpandFrameFields(OI_CODEC_SBC_FRAME_INFO *frame)
|
||||
{
|
||||
frame->nrof_blocks = block_values[frame->blocks];
|
||||
frame->nrof_subbands = band_values[frame->subbands];
|
||||
|
||||
frame->frequency = freq_values[frame->freqIndex];
|
||||
frame->nrof_channels = channel_values[frame->mode];
|
||||
}
|
||||
|
||||
/**
|
||||
* Unrolled macro to copy 4 32-bit aligned 32-bit values backward in memory
|
||||
*/
|
||||
#define COPY4WORDS_BACK(_dest, _src) \
|
||||
do { \
|
||||
OI_INT32 _a, _b, _c, _d; \
|
||||
_a = *--_src; \
|
||||
_b = *--_src; \
|
||||
_c = *--_src; \
|
||||
_d = *--_src; \
|
||||
*--_dest = _a; \
|
||||
*--_dest = _b; \
|
||||
*--_dest = _c; \
|
||||
*--_dest = _d; \
|
||||
} while (0)
|
||||
|
||||
|
||||
#if defined(USE_PLATFORM_MEMMOVE) || defined(USE_PLATFORM_MEMCPY)
|
||||
#include <string.h>
|
||||
#endif
|
||||
PRIVATE void shift_buffer(SBC_BUFFER_T *dest, SBC_BUFFER_T *src, OI_UINT wordCount)
|
||||
{
|
||||
#ifdef USE_PLATFORM_MEMMOVE
|
||||
memmove(dest, src, wordCount * sizeof(SBC_BUFFER_T));
|
||||
#elif defined(USE_PLATFORM_MEMCPY)
|
||||
OI_ASSERT(((OI_CHAR *)(dest) - (OI_CHAR *)(src)) >= wordCount*sizeof(*dest));
|
||||
memcpy(dest, src, wordCount * sizeof(SBC_BUFFER_T));
|
||||
#else
|
||||
OI_UINT n;
|
||||
OI_INT32 *d;
|
||||
OI_INT32 *s;
|
||||
n = wordCount / 4 / (sizeof(OI_INT32)/sizeof(*dest));
|
||||
OI_ASSERT((n * 4 * (sizeof(OI_INT32)/sizeof(*dest))) == wordCount);
|
||||
|
||||
d = (OI_INT32*)(dest + wordCount);
|
||||
s = (OI_INT32*)(src + wordCount);
|
||||
|
||||
do {
|
||||
COPY4WORDS_BACK(d, s);
|
||||
} while (--n);
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
@@ -0,0 +1,57 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2002 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
@file
|
||||
This file contains a single function, which returns a string indicating the
|
||||
version number of the eSBC codec
|
||||
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**
|
||||
@addtogroup codec_internal
|
||||
@{
|
||||
*/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
#include "oi_stddefs.h"
|
||||
#include "oi_codec_sbc_private.h"
|
||||
|
||||
/** Version string for the BLUEmagic 3.0 protocol stack and profiles */
|
||||
PRIVATE const OI_CHAR * codecVersion = "v1.5"
|
||||
#ifdef OI_SBC_EVAL
|
||||
" (Evaluation version)"
|
||||
#endif
|
||||
;
|
||||
|
||||
/** This function returns the version string for the BLUEmagic 3.0 protocol stack
|
||||
and profiles */
|
||||
const OI_CHAR *OI_CODEC_Version(void) {
|
||||
return codecVersion;
|
||||
}
|
||||
|
||||
/**********************************************************************************/
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
@@ -0,0 +1,111 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* @file readsamplesjoint.inc
|
||||
*
|
||||
* This is the body of the generic version of OI_SBC_ReadSamplesJoint().
|
||||
* It is designed to be \#included into a function as follows:
|
||||
\code
|
||||
void OI_SBC_ReadSamplesJoint4(OI_CODEC_SBC_COMMON_CONTEXT *common, OI_BITSTREAM *global_bs)
|
||||
{
|
||||
#define NROF_SUBBANDS 4
|
||||
#include "readsamplesjoint.inc"
|
||||
#undef NROF_SUBBANDS
|
||||
}
|
||||
|
||||
void OI_SBC_ReadSamplesJoint8(OI_CODEC_SBC_COMMON_CONTEXT *common, OI_BITSTREAM *global_bs)
|
||||
{
|
||||
#define NROF_SUBBANDS 8
|
||||
#include "readsamplesjoint.inc"
|
||||
#undef NROF_SUBBANDS
|
||||
}
|
||||
\endcode
|
||||
* Or to make a generic version:
|
||||
\code
|
||||
void OI_SBC_ReadSamplesJoint(OI_CODEC_SBC_COMMON_CONTEXT *common, OI_BITSTREAM *global_bs)
|
||||
{
|
||||
OI_UINT nrof_subbands = common->frameInfo.nrof_subbands;
|
||||
|
||||
#define NROF_SUBBANDS nrof_subbands
|
||||
#include "readsamplesjoint.inc"
|
||||
#undef NROF_SUBBANDS
|
||||
}
|
||||
\endcode
|
||||
* @ingroup codec_internal
|
||||
*******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
{
|
||||
OI_CODEC_SBC_COMMON_CONTEXT *common = &context->common;
|
||||
OI_UINT bl = common->frameInfo.nrof_blocks;
|
||||
OI_INT32 * RESTRICT s = common->subdata;
|
||||
OI_UINT8 *ptr = global_bs->ptr.w;
|
||||
OI_UINT32 value = global_bs->value;
|
||||
OI_UINT bitPtr = global_bs->bitPtr;
|
||||
OI_UINT8 jmask = common->frameInfo.join << (8 - NROF_SUBBANDS);
|
||||
|
||||
do {
|
||||
OI_INT8 *sf_array = &common->scale_factor[0];
|
||||
OI_UINT8 *bits_array = &common->bits.uint8[0];
|
||||
OI_UINT8 joint = jmask;
|
||||
OI_UINT sb;
|
||||
/*
|
||||
* Left channel
|
||||
*/
|
||||
sb = NROF_SUBBANDS;
|
||||
do {
|
||||
OI_UINT32 raw;
|
||||
OI_INT32 dequant;
|
||||
OI_UINT8 bits = *bits_array++;
|
||||
OI_INT sf = *sf_array++;
|
||||
|
||||
OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr);
|
||||
dequant = OI_SBC_Dequant(raw, sf, bits);
|
||||
*s++ = dequant;
|
||||
} while (--sb);
|
||||
/*
|
||||
* Right channel
|
||||
*/
|
||||
sb = NROF_SUBBANDS;
|
||||
do {
|
||||
OI_UINT32 raw;
|
||||
OI_INT32 dequant;
|
||||
OI_UINT8 bits = *bits_array++;
|
||||
OI_INT sf = *sf_array++;
|
||||
|
||||
OI_BITSTREAM_READUINT(raw, bits, ptr, value, bitPtr);
|
||||
dequant = OI_SBC_Dequant(raw, sf, bits);
|
||||
/*
|
||||
* Check if we need to do mid/side
|
||||
*/
|
||||
if (joint & 0x80) {
|
||||
OI_INT32 mid = *(s - NROF_SUBBANDS);
|
||||
OI_INT32 side = dequant;
|
||||
*(s - NROF_SUBBANDS) = mid + side;
|
||||
dequant = mid - side;
|
||||
}
|
||||
joint <<= 1;
|
||||
*s++ = dequant;
|
||||
} while (--sb);
|
||||
} while (--bl);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**
|
||||
@file
|
||||
|
||||
DO NOT EDIT THIS FILE DIRECTLY
|
||||
|
||||
This file is automatically generated by the "synthesis-gen.pl" script.
|
||||
Any changes to this generated file will be lost when the script is re-run.
|
||||
|
||||
These functions are called by functions in synthesis.c to perform the synthesis
|
||||
filterbank computations for the SBC decoder.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include <oi_codec_sbc_private.h>
|
||||
|
||||
#ifndef CLIP_INT16
|
||||
#define CLIP_INT16(x) do { if (x > OI_INT16_MAX) { x = OI_INT16_MAX; } else if (x < OI_INT16_MIN) { x = OI_INT16_MIN; } } while (0)
|
||||
#endif
|
||||
|
||||
#define MUL_16S_16S(_x, _y) ((_x) * (_y))
|
||||
|
||||
PRIVATE void SynthWindow80_generated(OI_INT16 *pcm, SBC_BUFFER_T const * RESTRICT buffer, OI_UINT strideShift);
|
||||
PRIVATE void SynthWindow80_generated(OI_INT16 *pcm, SBC_BUFFER_T const * RESTRICT buffer, OI_UINT strideShift)
|
||||
{
|
||||
OI_INT32 pcm_a, pcm_b;
|
||||
/* 1 - stage 0 */ pcm_b = 0;
|
||||
/* 1 - stage 0 */ pcm_b +=(MUL_16S_16S(8235, buffer[ 12]))>> 3;
|
||||
/* 1 - stage 0 */ pcm_b +=(MUL_16S_16S(-23167, buffer[ 20]))>> 3;
|
||||
/* 1 - stage 0 */ pcm_b +=(MUL_16S_16S(26479, buffer[ 28]))>> 2;
|
||||
/* 1 - stage 0 */ pcm_b +=(MUL_16S_16S(-17397, buffer[ 36]))<< 1;
|
||||
/* 1 - stage 0 */ pcm_b +=(MUL_16S_16S(9399, buffer[ 44]))<< 3;
|
||||
/* 1 - stage 0 */ pcm_b +=(MUL_16S_16S(17397, buffer[ 52]))<< 1;
|
||||
/* 1 - stage 0 */ pcm_b +=(MUL_16S_16S(26479, buffer[ 60]))>> 2;
|
||||
/* 1 - stage 0 */ pcm_b +=(MUL_16S_16S(23167, buffer[ 68]))>> 3;
|
||||
/* 1 - stage 0 */ pcm_b +=(MUL_16S_16S(8235, buffer[ 76]))>> 3;
|
||||
/* 1 - stage 0 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[(uint32_t)(0<<strideShift)] = (OI_INT16)pcm_b;
|
||||
/* 1 - stage 1 */ pcm_a = 0;
|
||||
/* 1 - stage 1 */ pcm_b = 0;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(-3263, buffer[ 5]))>> 5;
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(9293, buffer[ 5]))>> 3;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(29293, buffer[ 11]))>> 5;
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(-6087, buffer[ 11]))>> 2;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(-5229, buffer[ 21]));
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(1247, buffer[ 21]))<< 3;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(30835, buffer[ 27]))>> 3;
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(-2893, buffer[ 27]))<< 3;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(-27021, buffer[ 37]))<< 1;
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(23671, buffer[ 37]))<< 2;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(31633, buffer[ 43]))<< 1;
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(18055, buffer[ 43]))<< 1;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(17319, buffer[ 53]))<< 1;
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(11537, buffer[ 53]))>> 1;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(26663, buffer[ 59]))>> 2;
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(1747, buffer[ 59]))<< 1;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(4555, buffer[ 69]))>> 1;
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(685, buffer[ 69]))<< 1;
|
||||
/* 1 - stage 1 */ pcm_a +=(MUL_16S_16S(12419, buffer[ 75]))>> 4;
|
||||
/* 1 - stage 1 */ pcm_b +=(MUL_16S_16S(8721, buffer[ 75]))>> 7;
|
||||
/* 1 - stage 1 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[(uint32_t)(1<<strideShift)] = (OI_INT16)pcm_a;
|
||||
/* 1 - stage 1 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[(uint32_t)(7<<strideShift)] = (OI_INT16)pcm_b;
|
||||
/* 1 - stage 2 */ pcm_a = 0;
|
||||
/* 1 - stage 2 */ pcm_b = 0;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(-10385, buffer[ 6]))>> 6;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(11167, buffer[ 6]))>> 4;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(24995, buffer[ 10]))>> 5;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(-10337, buffer[ 10]))>> 4;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(-309, buffer[ 22]))<< 4;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(1917, buffer[ 22]))<< 2;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(9161, buffer[ 26]))>> 3;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(-30605, buffer[ 26]))>> 1;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(-23063, buffer[ 38]))<< 1;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(8317, buffer[ 38]))<< 3;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(27561, buffer[ 42]))<< 1;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(9553, buffer[ 42]))<< 2;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(2309, buffer[ 54]))<< 3;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(22117, buffer[ 54]))>> 4;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(12705, buffer[ 58]))>> 1;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(16383, buffer[ 58]))>> 2;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(6239, buffer[ 70]))>> 3;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(7543, buffer[ 70]))>> 3;
|
||||
/* 1 - stage 2 */ pcm_a +=(MUL_16S_16S(9251, buffer[ 74]))>> 4;
|
||||
/* 1 - stage 2 */ pcm_b +=(MUL_16S_16S(8603, buffer[ 74]))>> 6;
|
||||
/* 1 - stage 2 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[(uint32_t)(2<<strideShift)] = (OI_INT16)pcm_a;
|
||||
/* 1 - stage 2 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[(uint32_t)(6<<strideShift)] = (OI_INT16)pcm_b;
|
||||
/* 1 - stage 3 */ pcm_a = 0;
|
||||
/* 1 - stage 3 */ pcm_b = 0;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(-16457, buffer[ 7]))>> 6;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(16913, buffer[ 7]))>> 5;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(19083, buffer[ 9]))>> 5;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(-8443, buffer[ 9]))>> 7;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(-23641, buffer[ 23]))>> 2;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(3687, buffer[ 23]))<< 1;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(-29015, buffer[ 25]))>> 4;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(-301, buffer[ 25]))<< 5;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(-12889, buffer[ 39]))<< 2;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(15447, buffer[ 39]))<< 2;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(6145, buffer[ 41]))<< 3;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(10255, buffer[ 41]))<< 2;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(24211, buffer[ 55]))>> 1;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(-18233, buffer[ 55]))>> 3;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(23469, buffer[ 57]))>> 2;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(9405, buffer[ 57]))>> 1;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(21223, buffer[ 71]))>> 8;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(1499, buffer[ 71]))>> 1;
|
||||
/* 1 - stage 3 */ pcm_a +=(MUL_16S_16S(26913, buffer[ 73]))>> 6;
|
||||
/* 1 - stage 3 */ pcm_b +=(MUL_16S_16S(26189, buffer[ 73]))>> 7;
|
||||
/* 1 - stage 3 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[(uint32_t)(3<<strideShift)] = (OI_INT16)pcm_a;
|
||||
/* 1 - stage 3 */ pcm_b /= 32768; CLIP_INT16(pcm_b); pcm[(uint32_t)(5<<strideShift)] = (OI_INT16)pcm_b;
|
||||
/* 1 - stage 4 */ pcm_a = 0;
|
||||
/* 1 - stage 4 */ pcm_a +=(MUL_16S_16S(10445, buffer[ 8]))>> 4;
|
||||
/* 1 - stage 4 */ pcm_a +=(MUL_16S_16S(-5297, buffer[ 24]))<< 1;
|
||||
/* 1 - stage 4 */ pcm_a +=(MUL_16S_16S(22299, buffer[ 40]))<< 2;
|
||||
/* 1 - stage 4 */ pcm_a +=(MUL_16S_16S(10603, buffer[ 56]));
|
||||
/* 1 - stage 4 */ pcm_a +=(MUL_16S_16S(9539, buffer[ 72]))>> 4;
|
||||
/* 1 - stage 4 */ pcm_a /= 32768; CLIP_INT16(pcm_a); pcm[(uint32_t)(4<<strideShift)] = (OI_INT16)pcm_a;
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/** @file
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**@addgroup codec_internal*/
|
||||
/**@{*/
|
||||
|
||||
/*
|
||||
* Performs an 8-point Type-II scaled DCT using the Arai-Agui-Nakajima
|
||||
* factorization. The scaling factors are folded into the windowing
|
||||
* constants. 29 adds and 5 16x32 multiplies per 8 samples.
|
||||
*/
|
||||
|
||||
#include "oi_codec_sbc_private.h"
|
||||
|
||||
#define AAN_C4_FIX (759250125)/* S1.30 759250125 0.707107*/
|
||||
|
||||
#define AAN_C6_FIX (410903207)/* S1.30 410903207 0.382683*/
|
||||
|
||||
#define AAN_Q0_FIX (581104888)/* S1.30 581104888 0.541196*/
|
||||
|
||||
#define AAN_Q1_FIX (1402911301)/* S1.30 1402911301 1.306563*/
|
||||
|
||||
/** Scales x by y bits to the right, adding a rounding factor.
|
||||
*/
|
||||
#ifndef SCALE
|
||||
#define SCALE(x, y) (((x) + (1 <<((y)-1))) >> (y))
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Default C language implementation of a 32x32->32 multiply. This function may
|
||||
* be replaced by a platform-specific version for speed.
|
||||
*
|
||||
* @param u A signed 32-bit multiplicand
|
||||
* @param v A signed 32-bit multiplier
|
||||
|
||||
* @return A signed 32-bit value corresponding to the 32 most significant bits
|
||||
* of the 64-bit product of u and v.
|
||||
*/
|
||||
INLINE OI_INT32 default_mul_32s_32s_hi(OI_INT32 u, OI_INT32 v);
|
||||
INLINE OI_INT32 default_mul_32s_32s_hi(OI_INT32 u, OI_INT32 v)
|
||||
{
|
||||
OI_UINT32 u0, v0;
|
||||
OI_INT32 u1, v1, w1, w2, t;
|
||||
|
||||
u0 = u & 0xFFFF; u1 = u >> 16;
|
||||
v0 = v & 0xFFFF; v1 = v >> 16;
|
||||
t = u0*v0;
|
||||
t = (u1*v0) + ((OI_UINT32)t >> 16);
|
||||
w1 = t & 0xFFFF;
|
||||
w2 = t >> 16;
|
||||
w1 = (u0*v1) + w1;
|
||||
return (u1*v1) + w2 + (w1 >> 16);
|
||||
}
|
||||
|
||||
#define MUL_32S_32S_HI(_x, _y) default_mul_32s_32s_hi(_x, _y)
|
||||
|
||||
|
||||
#ifdef DEBUG_DCT
|
||||
PRIVATE void float_dct2_8(float * RESTRICT out, OI_INT32 const *RESTRICT in)
|
||||
{
|
||||
#define FIX(x,bits) (((int)floor(0.5f+((x)*((float)(1<<bits)))))/((float)(1<<bits)))
|
||||
#define FLOAT_BUTTERFLY(x,y) x += y; y = x - (y*2); OI_ASSERT(VALID_INT32(x)); OI_ASSERT(VALID_INT32(y));
|
||||
#define FLOAT_MULT_DCT(K, sample) (FIX(K,20) * sample)
|
||||
#define FLOAT_SCALE(x, y) (((x) / (double)(1 << (y))))
|
||||
|
||||
double L00,L01,L02,L03,L04,L05,L06,L07;
|
||||
double L25;
|
||||
|
||||
double in0,in1,in2,in3;
|
||||
double in4,in5,in6,in7;
|
||||
|
||||
in0 = FLOAT_SCALE(in[0], DCTII_8_SHIFT_IN); OI_ASSERT(VALID_INT32(in0));
|
||||
in1 = FLOAT_SCALE(in[1], DCTII_8_SHIFT_IN); OI_ASSERT(VALID_INT32(in1));
|
||||
in2 = FLOAT_SCALE(in[2], DCTII_8_SHIFT_IN); OI_ASSERT(VALID_INT32(in2));
|
||||
in3 = FLOAT_SCALE(in[3], DCTII_8_SHIFT_IN); OI_ASSERT(VALID_INT32(in3));
|
||||
in4 = FLOAT_SCALE(in[4], DCTII_8_SHIFT_IN); OI_ASSERT(VALID_INT32(in4));
|
||||
in5 = FLOAT_SCALE(in[5], DCTII_8_SHIFT_IN); OI_ASSERT(VALID_INT32(in5));
|
||||
in6 = FLOAT_SCALE(in[6], DCTII_8_SHIFT_IN); OI_ASSERT(VALID_INT32(in6));
|
||||
in7 = FLOAT_SCALE(in[7], DCTII_8_SHIFT_IN); OI_ASSERT(VALID_INT32(in7));
|
||||
|
||||
L00 = (in0 + in7); OI_ASSERT(VALID_INT32(L00));
|
||||
L01 = (in1 + in6); OI_ASSERT(VALID_INT32(L01));
|
||||
L02 = (in2 + in5); OI_ASSERT(VALID_INT32(L02));
|
||||
L03 = (in3 + in4); OI_ASSERT(VALID_INT32(L03));
|
||||
|
||||
L04 = (in3 - in4); OI_ASSERT(VALID_INT32(L04));
|
||||
L05 = (in2 - in5); OI_ASSERT(VALID_INT32(L05));
|
||||
L06 = (in1 - in6); OI_ASSERT(VALID_INT32(L06));
|
||||
L07 = (in0 - in7); OI_ASSERT(VALID_INT32(L07));
|
||||
|
||||
FLOAT_BUTTERFLY(L00, L03);
|
||||
FLOAT_BUTTERFLY(L01, L02);
|
||||
|
||||
L02 += L03; OI_ASSERT(VALID_INT32(L02));
|
||||
|
||||
L02 = FLOAT_MULT_DCT(AAN_C4_FLOAT, L02); OI_ASSERT(VALID_INT32(L02));
|
||||
|
||||
FLOAT_BUTTERFLY(L00, L01);
|
||||
|
||||
out[0] = (float)FLOAT_SCALE(L00, DCTII_8_SHIFT_0); OI_ASSERT(VALID_INT16(out[0]));
|
||||
out[4] = (float)FLOAT_SCALE(L01, DCTII_8_SHIFT_4); OI_ASSERT(VALID_INT16(out[4]));
|
||||
|
||||
FLOAT_BUTTERFLY(L03, L02);
|
||||
out[6] = (float)FLOAT_SCALE(L02, DCTII_8_SHIFT_6); OI_ASSERT(VALID_INT16(out[6]));
|
||||
out[2] = (float)FLOAT_SCALE(L03, DCTII_8_SHIFT_2); OI_ASSERT(VALID_INT16(out[2]));
|
||||
|
||||
L04 += L05; OI_ASSERT(VALID_INT32(L04));
|
||||
L05 += L06; OI_ASSERT(VALID_INT32(L05));
|
||||
L06 += L07; OI_ASSERT(VALID_INT32(L06));
|
||||
|
||||
L04/=2;
|
||||
L05/=2;
|
||||
L06/=2;
|
||||
L07/=2;
|
||||
|
||||
L05 = FLOAT_MULT_DCT(AAN_C4_FLOAT, L05); OI_ASSERT(VALID_INT32(L05));
|
||||
|
||||
L25 = L06 - L04; OI_ASSERT(VALID_INT32(L25));
|
||||
L25 = FLOAT_MULT_DCT(AAN_C6_FLOAT, L25); OI_ASSERT(VALID_INT32(L25));
|
||||
|
||||
L04 = FLOAT_MULT_DCT(AAN_Q0_FLOAT, L04); OI_ASSERT(VALID_INT32(L04));
|
||||
L04 -= L25; OI_ASSERT(VALID_INT32(L04));
|
||||
|
||||
L06 = FLOAT_MULT_DCT(AAN_Q1_FLOAT, L06); OI_ASSERT(VALID_INT32(L06));
|
||||
L06 -= L25; OI_ASSERT(VALID_INT32(L25));
|
||||
|
||||
FLOAT_BUTTERFLY(L07, L05);
|
||||
|
||||
FLOAT_BUTTERFLY(L05, L04);
|
||||
out[3] = (float)(FLOAT_SCALE(L04, DCTII_8_SHIFT_3-1)); OI_ASSERT(VALID_INT16(out[3]));
|
||||
out[5] = (float)(FLOAT_SCALE(L05, DCTII_8_SHIFT_5-1)); OI_ASSERT(VALID_INT16(out[5]));
|
||||
|
||||
FLOAT_BUTTERFLY(L07, L06);
|
||||
out[7] = (float)(FLOAT_SCALE(L06, DCTII_8_SHIFT_7-1)); OI_ASSERT(VALID_INT16(out[7]));
|
||||
out[1] = (float)(FLOAT_SCALE(L07, DCTII_8_SHIFT_1-1)); OI_ASSERT(VALID_INT16(out[1]));
|
||||
}
|
||||
#undef BUTTERFLY
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* This function calculates the AAN DCT. Its inputs are in S16.15 format, as
|
||||
* returned by OI_SBC_Dequant. In practice, abs(in[x]) < 52429.0 / 1.38
|
||||
* (1244918057 integer). The function it computes is an approximation to the array defined
|
||||
* by:
|
||||
*
|
||||
* diag(aan_s) * AAN= C2
|
||||
*
|
||||
* or
|
||||
*
|
||||
* AAN = diag(1/aan_s) * C2
|
||||
*
|
||||
* where C2 is as it is defined in the comment at the head of this file, and
|
||||
*
|
||||
* aan_s[i] = aan_s = 1/(2*cos(i*pi/16)) with i = 1..7, aan_s[0] = 1;
|
||||
*
|
||||
* aan_s[i] = [ 1.000 0.510 0.541 0.601 0.707 0.900 1.307 2.563 ]
|
||||
*
|
||||
* The output ranges are shown as follows:
|
||||
*
|
||||
* Let Y[0..7] = AAN * X[0..7]
|
||||
*
|
||||
* Without loss of generality, assume the input vector X consists of elements
|
||||
* between -1 and 1. The maximum possible value of a given output element occurs
|
||||
* with some particular combination of input vector elements each of which is -1
|
||||
* or 1. Consider the computation of Y[i]. Y[i] = sum t=0..7 of AAN[t,i]*X[i]. Y is
|
||||
* maximized if the sign of X[i] matches the sign of AAN[t,i], ensuring a
|
||||
* positive contribution to the sum. Equivalently, one may simply sum
|
||||
* abs(AAN)[t,i] over t to get the maximum possible value of Y[i].
|
||||
*
|
||||
* This yields approximately [8.00 10.05 9.66 8.52 8.00 5.70 4.00 2.00]
|
||||
*
|
||||
* Given the maximum magnitude sensible input value of +/-37992, this yields the
|
||||
* following vector of maximum output magnitudes:
|
||||
*
|
||||
* [ 303936 381820 367003 323692 303936 216555 151968 75984 ]
|
||||
*
|
||||
* Ultimately, these values must fit into 16 bit signed integers, so they must
|
||||
* be scaled. A non-uniform scaling helps maximize the kept precision. The
|
||||
* relative number of extra bits of precision maintainable with respect to the
|
||||
* largest value is given here:
|
||||
*
|
||||
* [ 0 0 0 0 0 0 1 2 ]
|
||||
*
|
||||
*/
|
||||
PRIVATE void dct2_8(SBC_BUFFER_T * RESTRICT out, OI_INT32 const *RESTRICT in);
|
||||
PRIVATE void dct2_8(SBC_BUFFER_T * RESTRICT out, OI_INT32 const *RESTRICT in)
|
||||
{
|
||||
#define BUTTERFLY(x,y) x += y; y = x - (y<<1);
|
||||
#define FIX_MULT_DCT(K, x) (MUL_32S_32S_HI(K,x)<<2)
|
||||
|
||||
OI_INT32 L00,L01,L02,L03,L04,L05,L06,L07;
|
||||
OI_INT32 L25;
|
||||
|
||||
OI_INT32 in0,in1,in2,in3;
|
||||
OI_INT32 in4,in5,in6,in7;
|
||||
|
||||
#if DCTII_8_SHIFT_IN != 0
|
||||
in0 = SCALE(in[0], DCTII_8_SHIFT_IN);
|
||||
in1 = SCALE(in[1], DCTII_8_SHIFT_IN);
|
||||
in2 = SCALE(in[2], DCTII_8_SHIFT_IN);
|
||||
in3 = SCALE(in[3], DCTII_8_SHIFT_IN);
|
||||
in4 = SCALE(in[4], DCTII_8_SHIFT_IN);
|
||||
in5 = SCALE(in[5], DCTII_8_SHIFT_IN);
|
||||
in6 = SCALE(in[6], DCTII_8_SHIFT_IN);
|
||||
in7 = SCALE(in[7], DCTII_8_SHIFT_IN);
|
||||
#else
|
||||
in0 = in[0];
|
||||
in1 = in[1];
|
||||
in2 = in[2];
|
||||
in3 = in[3];
|
||||
in4 = in[4];
|
||||
in5 = in[5];
|
||||
in6 = in[6];
|
||||
in7 = in[7];
|
||||
#endif
|
||||
|
||||
L00 = in0 + in7;
|
||||
L01 = in1 + in6;
|
||||
L02 = in2 + in5;
|
||||
L03 = in3 + in4;
|
||||
|
||||
L04 = in3 - in4;
|
||||
L05 = in2 - in5;
|
||||
L06 = in1 - in6;
|
||||
L07 = in0 - in7;
|
||||
|
||||
BUTTERFLY(L00, L03);
|
||||
BUTTERFLY(L01, L02);
|
||||
|
||||
L02 += L03;
|
||||
|
||||
L02 = FIX_MULT_DCT(AAN_C4_FIX, L02);
|
||||
|
||||
BUTTERFLY(L00, L01);
|
||||
|
||||
out[0] = (OI_INT16)SCALE(L00, DCTII_8_SHIFT_0);
|
||||
out[4] = (OI_INT16)SCALE(L01, DCTII_8_SHIFT_4);
|
||||
|
||||
BUTTERFLY(L03, L02);
|
||||
out[6] = (OI_INT16)SCALE(L02, DCTII_8_SHIFT_6);
|
||||
out[2] = (OI_INT16)SCALE(L03, DCTII_8_SHIFT_2);
|
||||
|
||||
L04 += L05;
|
||||
L05 += L06;
|
||||
L06 += L07;
|
||||
|
||||
L04/=2;
|
||||
L05/=2;
|
||||
L06/=2;
|
||||
L07/=2;
|
||||
|
||||
L05 = FIX_MULT_DCT(AAN_C4_FIX, L05);
|
||||
|
||||
L25 = L06 - L04;
|
||||
L25 = FIX_MULT_DCT(AAN_C6_FIX, L25);
|
||||
|
||||
L04 = FIX_MULT_DCT(AAN_Q0_FIX, L04);
|
||||
L04 -= L25;
|
||||
|
||||
L06 = FIX_MULT_DCT(AAN_Q1_FIX, L06);
|
||||
L06 -= L25;
|
||||
|
||||
BUTTERFLY(L07, L05);
|
||||
|
||||
BUTTERFLY(L05, L04);
|
||||
out[3] = (OI_INT16)SCALE(L04, DCTII_8_SHIFT_3-1);
|
||||
out[5] = (OI_INT16)SCALE(L05, DCTII_8_SHIFT_5-1);
|
||||
|
||||
BUTTERFLY(L07, L06);
|
||||
out[7] = (OI_INT16)SCALE(L06, DCTII_8_SHIFT_7-1);
|
||||
out[1] = (OI_INT16)SCALE(L07, DCTII_8_SHIFT_1-1);
|
||||
#undef BUTTERFLY
|
||||
|
||||
#ifdef DEBUG_DCT
|
||||
{
|
||||
float float_out[8];
|
||||
float_dct2_8(float_out, in);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**@}*/
|
||||
@@ -0,0 +1,513 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
* Copyright 2003 - 2004 Open Interface North America, Inc. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/**********************************************************************************
|
||||
$Revision: #1 $
|
||||
***********************************************************************************/
|
||||
|
||||
/** @file
|
||||
|
||||
This file, along with synthesis-generated.c, contains the synthesis
|
||||
filterbank routines. The operations performed correspond to the
|
||||
operations described in A2DP Appendix B, Figure 12.3. Several
|
||||
mathematical optimizations are performed, particularly for the
|
||||
8-subband case.
|
||||
|
||||
One important optimization is to note that the "matrixing" operation
|
||||
can be decomposed into the product of a type II discrete cosine kernel
|
||||
and another, sparse matrix.
|
||||
|
||||
According to Fig 12.3, in the 8-subband case,
|
||||
@code
|
||||
N[k][i] = cos((i+0.5)*(k+4)*pi/8), k = 0..15 and i = 0..7
|
||||
@endcode
|
||||
|
||||
N can be factored as R * C2, where C2 is an 8-point type II discrete
|
||||
cosine kernel given by
|
||||
@code
|
||||
C2[k][i] = cos((i+0.5)*k*pi/8)), k = 0..7 and i = 0..7
|
||||
@endcode
|
||||
|
||||
R turns out to be a sparse 16x8 matrix with the following non-zero
|
||||
entries:
|
||||
@code
|
||||
R[k][k+4] = 1, k = 0..3
|
||||
R[k][abs(12-k)] = -1, k = 5..15
|
||||
@endcode
|
||||
|
||||
The spec describes computing V[0..15] as N * R.
|
||||
@code
|
||||
V[0..15] = N * R = (R * C2) * R = R * (C2 * R)
|
||||
@endcode
|
||||
|
||||
C2 * R corresponds to computing the discrete cosine transform of R, so
|
||||
V[0..15] can be computed by taking the DCT of R followed by assignment
|
||||
and selective negation of the DCT result into V.
|
||||
|
||||
Although this was derived empirically using GNU Octave, it is
|
||||
formally demonstrated in, e.g., Liu, Chi-Min and Lee,
|
||||
Wen-Chieh. "A Unified Fast Algorithm for Cosine Modulated
|
||||
Filter Banks in Current Audio Coding Standards." Journal of
|
||||
the AES 47 (December 1999): 1061.
|
||||
|
||||
Given the shift operation performed prior to computing V[0..15], it is
|
||||
clear that V[0..159] represents a rolling history of the 10 most
|
||||
recent groups of blocks input to the synthesis operation. Interpreting
|
||||
the matrix N in light of its factorization into C2 and R, R's
|
||||
sparseness has implications for interpreting the values in V. In
|
||||
particular, there is considerable redundancy in the values stored in
|
||||
V. Furthermore, since R[4][0..7] are all zeros, one out of every 16
|
||||
values in V will be zero regardless of the input data. Within each
|
||||
block of 16 values in V, fully half of them are redundant or
|
||||
irrelevant:
|
||||
|
||||
@code
|
||||
V[ 0] = DCT[4]
|
||||
V[ 1] = DCT[5]
|
||||
V[ 2] = DCT[6]
|
||||
V[ 3] = DCT[7]
|
||||
V[ 4] = 0
|
||||
V[ 5] = -DCT[7] = -V[3] (redundant)
|
||||
V[ 6] = -DCT[6] = -V[2] (redundant)
|
||||
V[ 7] = -DCT[5] = -V[1] (redundant)
|
||||
V[ 8] = -DCT[4] = -V[0] (redundant)
|
||||
V[ 9] = -DCT[3]
|
||||
V[10] = -DCT[2]
|
||||
V[11] = -DCT[1]
|
||||
V[12] = -DCT[0]
|
||||
V[13] = -DCT[1] = V[11] (redundant)
|
||||
V[14] = -DCT[2] = V[10] (redundant)
|
||||
V[15] = -DCT[3] = V[ 9] (redundant)
|
||||
@endcode
|
||||
|
||||
Since the elements of V beyond 15 were originally computed the same
|
||||
way during a previous run, what holds true for V[x] also holds true
|
||||
for V[x+16]. Thus, so long as care is taken to maintain the mapping,
|
||||
we need only actually store the unique values, which correspond to the
|
||||
output of the DCT, in some cases inverted. In fact, instead of storing
|
||||
V[0..159], we could store DCT[0..79] which would contain a history of
|
||||
DCT results. More on this in a bit.
|
||||
|
||||
Going back to figure 12.3 in the spec, it should be clear that the
|
||||
vector U need not actually be explicitly constructed, but that with
|
||||
suitable indexing into V during the window operation, the same end can
|
||||
be accomplished. In the same spirit of the pseudocode shown in the
|
||||
figure, the following is the construction of W without using U:
|
||||
|
||||
@code
|
||||
for i=0 to 79 do
|
||||
W[i] = D[i]*VSIGN(i)*V[remap_V(i)] where remap_V(i) = 32*(int(i/16)) + (i % 16) + (i % 16 >= 8 ? 16 : 0)
|
||||
and VSIGN(i) maps i%16 into {1, 1, 1, 1, 0, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1 }
|
||||
These values correspond to the
|
||||
signs of the redundant values as
|
||||
shown in the explanation three
|
||||
paragraphs above.
|
||||
@endcode
|
||||
|
||||
We saw above how V[4..8,13..15] (and by extension
|
||||
V[(4..8,13..15)+16*n]) can be defined in terms of other elements
|
||||
within the subblock of V. V[0..3,9..12] correspond to DCT elements.
|
||||
|
||||
@code
|
||||
for i=0 to 79 do
|
||||
W[i] = D[i]*DSIGN(i)*DCT[remap_DCT(i)]
|
||||
@endcode
|
||||
|
||||
The DCT is calculated using the Arai-Agui-Nakajima factorization,
|
||||
which saves some computation by producing output that needs to be
|
||||
multiplied by scaling factors before being used.
|
||||
|
||||
@code
|
||||
for i=0 to 79 do
|
||||
W[i] = D[i]*SCALE[i%8]*AAN_DCT[remap_DCT(i)]
|
||||
@endcode
|
||||
|
||||
D can be premultiplied with the DCT scaling factors to yield
|
||||
|
||||
@code
|
||||
for i=0 to 79 do
|
||||
W[i] = DSCALED[i]*AAN_DCT[remap_DCT(i)] where DSCALED[i] = D[i]*SCALE[i%8]
|
||||
@endcode
|
||||
|
||||
The output samples X[0..7] are defined as sums of W:
|
||||
|
||||
@code
|
||||
X[j] = sum{i=0..9}(W[j+8*i])
|
||||
@endcode
|
||||
|
||||
@ingroup codec_internal
|
||||
*/
|
||||
|
||||
/**
|
||||
@addtogroup codec_internal
|
||||
@{
|
||||
*/
|
||||
|
||||
#include "oi_codec_sbc_private.h"
|
||||
|
||||
const OI_INT32 dec_window_4[21] = {
|
||||
0, /* +0.00000000E+00 */
|
||||
97, /* +5.36548976E-04 */
|
||||
270, /* +1.49188357E-03 */
|
||||
495, /* +2.73370904E-03 */
|
||||
694, /* +3.83720193E-03 */
|
||||
704, /* +3.89205149E-03 */
|
||||
338, /* +1.86581691E-03 */
|
||||
-554, /* -3.06012286E-03 */
|
||||
1974, /* +1.09137620E-02 */
|
||||
3697, /* +2.04385087E-02 */
|
||||
5224, /* +2.88757392E-02 */
|
||||
5824, /* +3.21939290E-02 */
|
||||
4681, /* +2.58767811E-02 */
|
||||
1109, /* +6.13245186E-03 */
|
||||
-5214, /* -2.88217274E-02 */
|
||||
-14047, /* -7.76463494E-02 */
|
||||
24529, /* +1.35593274E-01 */
|
||||
35274, /* +1.94987841E-01 */
|
||||
44618, /* +2.46636662E-01 */
|
||||
50984, /* +2.81828203E-01 */
|
||||
53243, /* +2.94315332E-01 */
|
||||
};
|
||||
|
||||
#define DCTII_4_K06_FIX ( 11585)/* S1.14 11585 0.707107*/
|
||||
|
||||
#define DCTII_4_K08_FIX ( 21407)/* S1.14 21407 1.306563*/
|
||||
|
||||
#define DCTII_4_K09_FIX (-15137)/* S1.14 -15137 -0.923880*/
|
||||
|
||||
#define DCTII_4_K10_FIX ( -8867)/* S1.14 -8867 -0.541196*/
|
||||
|
||||
/** Scales x by y bits to the right, adding a rounding factor.
|
||||
*/
|
||||
#ifndef SCALE
|
||||
#define SCALE(x, y) (((x) + (1 <<((y)-1))) >> (y))
|
||||
#endif
|
||||
|
||||
#ifndef CLIP_INT16
|
||||
#define CLIP_INT16(x) do { if (x > OI_INT16_MAX) { x = OI_INT16_MAX; } else if (x < OI_INT16_MIN) { x = OI_INT16_MIN; } } while (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Default C language implementation of a 16x32->32 multiply. This function may
|
||||
* be replaced by a platform-specific version for speed.
|
||||
*
|
||||
* @param u A signed 16-bit multiplicand
|
||||
* @param v A signed 32-bit multiplier
|
||||
|
||||
* @return A signed 32-bit value corresponding to the 32 most significant bits
|
||||
* of the 48-bit product of u and v.
|
||||
*/
|
||||
INLINE OI_INT32 default_mul_16s_32s_hi(OI_INT16 u, OI_INT32 v);
|
||||
INLINE OI_INT32 default_mul_16s_32s_hi(OI_INT16 u, OI_INT32 v)
|
||||
{
|
||||
OI_UINT16 v0;
|
||||
OI_INT16 v1;
|
||||
|
||||
OI_INT32 w,x;
|
||||
|
||||
v0 = (OI_UINT16)(v & 0xffff);
|
||||
v1 = (OI_INT16) (v >> 16);
|
||||
|
||||
w = v1 * u;
|
||||
x = u * v0;
|
||||
|
||||
return w + (x >> 16);
|
||||
}
|
||||
|
||||
#define MUL_16S_32S_HI(_x, _y) default_mul_16s_32s_hi(_x, _y)
|
||||
|
||||
#define LONG_MULT_DCT(K, sample) (MUL_16S_32S_HI(K, sample)<<2)
|
||||
|
||||
PRIVATE void SynthWindow80_generated(OI_INT16 *pcm, SBC_BUFFER_T const * RESTRICT buffer, OI_UINT strideShift);
|
||||
PRIVATE void SynthWindow112_generated(OI_INT16 *pcm, SBC_BUFFER_T const * RESTRICT buffer, OI_UINT strideShift);
|
||||
PRIVATE void dct2_8(SBC_BUFFER_T * RESTRICT out, OI_INT32 const * RESTRICT x);
|
||||
|
||||
typedef void (*SYNTH_FRAME)(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount);
|
||||
|
||||
#ifndef COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS
|
||||
#define COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(dest, src) do { shift_buffer(dest, src, 72); } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef DCT2_8
|
||||
#define DCT2_8(dst, src) dct2_8(dst, src)
|
||||
#endif
|
||||
|
||||
#ifndef SYNTH80
|
||||
#define SYNTH80 SynthWindow80_generated
|
||||
#endif
|
||||
|
||||
#ifndef SYNTH112
|
||||
#define SYNTH112 SynthWindow112_generated
|
||||
#endif
|
||||
|
||||
PRIVATE void OI_SBC_SynthFrame_80(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount);
|
||||
PRIVATE void OI_SBC_SynthFrame_80(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount)
|
||||
{
|
||||
OI_UINT blk;
|
||||
OI_UINT ch;
|
||||
OI_UINT nrof_channels = context->common.frameInfo.nrof_channels;
|
||||
OI_UINT pcmStrideShift = (context->common.pcmStride == 1) ? 0 : 1;
|
||||
OI_UINT offset = context->common.filterBufferOffset;
|
||||
OI_INT32 *s = context->common.subdata + (8 * nrof_channels * blkstart);
|
||||
OI_UINT blkstop = blkstart + blkcount;
|
||||
|
||||
for (blk = blkstart; blk < blkstop; blk++) {
|
||||
if (offset == 0) {
|
||||
COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[0] + context->common.filterBufferLen - 72, context->common.filterBuffer[0]);
|
||||
if (nrof_channels == 2) {
|
||||
COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[1] + context->common.filterBufferLen - 72, context->common.filterBuffer[1]);
|
||||
}
|
||||
offset = context->common.filterBufferLen - 80;
|
||||
} else {
|
||||
offset -= 1*8;
|
||||
}
|
||||
|
||||
for (ch = 0; ch < nrof_channels; ch++) {
|
||||
DCT2_8(context->common.filterBuffer[ch] + offset, s);
|
||||
SYNTH80(pcm + ch, context->common.filterBuffer[ch] + offset, pcmStrideShift);
|
||||
s += 8;
|
||||
}
|
||||
pcm += (8 << pcmStrideShift);
|
||||
}
|
||||
context->common.filterBufferOffset = offset;
|
||||
}
|
||||
|
||||
PRIVATE void OI_SBC_SynthFrame_4SB(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount);
|
||||
PRIVATE void OI_SBC_SynthFrame_4SB(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount)
|
||||
{
|
||||
OI_UINT blk;
|
||||
OI_UINT ch;
|
||||
OI_UINT nrof_channels = context->common.frameInfo.nrof_channels;
|
||||
OI_UINT pcmStrideShift = (context->common.pcmStride == 1) ? 0 : 1;
|
||||
OI_UINT offset = context->common.filterBufferOffset;
|
||||
OI_INT32 *s = context->common.subdata + (8 * nrof_channels * blkstart);
|
||||
OI_UINT blkstop = blkstart + blkcount;
|
||||
|
||||
for (blk = blkstart; blk < blkstop; blk++) {
|
||||
if (offset == 0) {
|
||||
COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[0] + context->common.filterBufferLen - 72,context->common.filterBuffer[0]);
|
||||
if (nrof_channels == 2) {
|
||||
COPY_BACKWARD_32BIT_ALIGNED_72_HALFWORDS(context->common.filterBuffer[1] + context->common.filterBufferLen - 72,context->common.filterBuffer[1]);
|
||||
}
|
||||
offset =context->common.filterBufferLen - 80;
|
||||
} else {
|
||||
offset -= 8;
|
||||
}
|
||||
for (ch = 0; ch < nrof_channels; ch++) {
|
||||
cosineModulateSynth4(context->common.filterBuffer[ch] + offset, s);
|
||||
SynthWindow40_int32_int32_symmetry_with_sum(pcm + ch,
|
||||
context->common.filterBuffer[ch] + offset,
|
||||
pcmStrideShift);
|
||||
s += 4;
|
||||
}
|
||||
pcm += (4 << pcmStrideShift);
|
||||
}
|
||||
context->common.filterBufferOffset = offset;
|
||||
}
|
||||
|
||||
#ifdef SBC_ENHANCED
|
||||
|
||||
PRIVATE void OI_SBC_SynthFrame_Enhanced(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT blkstart, OI_UINT blkcount)
|
||||
{
|
||||
OI_UINT blk;
|
||||
OI_UINT ch;
|
||||
OI_UINT nrof_channels = context->common.frameInfo.nrof_channels;
|
||||
OI_UINT pcmStrideShift = context->common.pcmStride == 1 ? 0 : 1;
|
||||
OI_UINT offset = context->common.filterBufferOffset;
|
||||
OI_INT32 *s = context->common.subdata + 8 * nrof_channels * blkstart;
|
||||
OI_UINT blkstop = blkstart + blkcount;
|
||||
|
||||
for (blk = blkstart; blk < blkstop; blk++) {
|
||||
if (offset == 0) {
|
||||
COPY_BACKWARD_32BIT_ALIGNED_104_HALFWORDS(context->common.filterBuffer[0] +context->common.filterBufferLen - 104, context->common.filterBuffer[0]);
|
||||
if (nrof_channels == 2) {
|
||||
COPY_BACKWARD_32BIT_ALIGNED_104_HALFWORDS(context->common.filterBuffer[1] + context->common.filterBufferLen - 104, context->common.filterBuffer[1]);
|
||||
}
|
||||
offset = context->common.filterBufferLen - 112;
|
||||
} else {
|
||||
offset -= 8;
|
||||
}
|
||||
for (ch = 0; ch < nrof_channels; ++ch) {
|
||||
DCT2_8(context->common.filterBuffer[ch] + offset, s);
|
||||
SYNTH112(pcm + ch, context->common.filterBuffer[ch] + offset, pcmStrideShift);
|
||||
s += 8;
|
||||
}
|
||||
pcm += (8 << pcmStrideShift);
|
||||
}
|
||||
context->common.filterBufferOffset = offset;
|
||||
}
|
||||
|
||||
static const SYNTH_FRAME SynthFrameEnhanced[] = {
|
||||
(SYNTH_FRAME) NULL, /* invalid */
|
||||
OI_SBC_SynthFrame_Enhanced, /* mono */
|
||||
OI_SBC_SynthFrame_Enhanced /* stereo */
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static const SYNTH_FRAME SynthFrame8SB[] = {
|
||||
(SYNTH_FRAME) NULL, /* invalid */
|
||||
OI_SBC_SynthFrame_80, /* mono */
|
||||
OI_SBC_SynthFrame_80 /* stereo */
|
||||
};
|
||||
|
||||
|
||||
static const SYNTH_FRAME SynthFrame4SB[] = {
|
||||
(SYNTH_FRAME) NULL, /* invalid */
|
||||
OI_SBC_SynthFrame_4SB, /* mono */
|
||||
OI_SBC_SynthFrame_4SB /* stereo */
|
||||
};
|
||||
|
||||
PRIVATE void OI_SBC_SynthFrame(OI_CODEC_SBC_DECODER_CONTEXT *context, OI_INT16 *pcm, OI_UINT start_block, OI_UINT nrof_blocks)
|
||||
{
|
||||
OI_UINT nrof_subbands = context->common.frameInfo.nrof_subbands;
|
||||
OI_UINT nrof_channels = context->common.frameInfo.nrof_channels;
|
||||
|
||||
OI_ASSERT(nrof_subbands == 4 || nrof_subbands == 8);
|
||||
if (nrof_subbands == 4) {
|
||||
SynthFrame4SB[nrof_channels](context, pcm, start_block, nrof_blocks);
|
||||
#ifdef SBC_ENHANCED
|
||||
} else if (context->common.frameInfo.enhanced) {
|
||||
SynthFrameEnhanced[nrof_channels](context, pcm, start_block, nrof_blocks);
|
||||
#endif /* SBC_ENHANCED */
|
||||
} else {
|
||||
SynthFrame8SB[nrof_channels](context, pcm, start_block, nrof_blocks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SynthWindow40_int32_int32_symmetry_with_sum(OI_INT16 *pcm, SBC_BUFFER_T buffer[80], OI_UINT strideShift)
|
||||
{
|
||||
OI_INT32 pa;
|
||||
OI_INT32 pb;
|
||||
|
||||
/* These values should be zero, since out[2] of the 4-band cosine modulation
|
||||
* is always zero. */
|
||||
OI_ASSERT(buffer[ 2] == 0);
|
||||
OI_ASSERT(buffer[10] == 0);
|
||||
OI_ASSERT(buffer[18] == 0);
|
||||
OI_ASSERT(buffer[26] == 0);
|
||||
OI_ASSERT(buffer[34] == 0);
|
||||
OI_ASSERT(buffer[42] == 0);
|
||||
OI_ASSERT(buffer[50] == 0);
|
||||
OI_ASSERT(buffer[58] == 0);
|
||||
OI_ASSERT(buffer[66] == 0);
|
||||
OI_ASSERT(buffer[74] == 0);
|
||||
|
||||
|
||||
pa = dec_window_4[ 4] * (buffer[12] + buffer[76]);
|
||||
pa += dec_window_4[ 8] * (buffer[16] - buffer[64]);
|
||||
pa += dec_window_4[12] * (buffer[28] + buffer[60]);
|
||||
pa += dec_window_4[16] * (buffer[32] - buffer[48]);
|
||||
pa += dec_window_4[20] * buffer[44];
|
||||
pa = SCALE(-pa, 15);
|
||||
CLIP_INT16(pa);
|
||||
pcm[(uint32_t)(0 << strideShift)] = (OI_INT16)pa;
|
||||
|
||||
|
||||
pa = dec_window_4[ 1] * buffer[ 1]; pb = dec_window_4[ 1] * buffer[79];
|
||||
pb += dec_window_4[ 3] * buffer[ 3]; pa += dec_window_4[ 3] * buffer[77];
|
||||
pa += dec_window_4[ 5] * buffer[13]; pb += dec_window_4[ 5] * buffer[67];
|
||||
pb += dec_window_4[ 7] * buffer[15]; pa += dec_window_4[ 7] * buffer[65];
|
||||
pa += dec_window_4[ 9] * buffer[17]; pb += dec_window_4[ 9] * buffer[63];
|
||||
pb += dec_window_4[11] * buffer[19]; pa += dec_window_4[11] * buffer[61];
|
||||
pa += dec_window_4[13] * buffer[29]; pb += dec_window_4[13] * buffer[51];
|
||||
pb += dec_window_4[15] * buffer[31]; pa += dec_window_4[15] * buffer[49];
|
||||
pa += dec_window_4[17] * buffer[33]; pb += dec_window_4[17] * buffer[47];
|
||||
pb += dec_window_4[19] * buffer[35]; pa += dec_window_4[19] * buffer[45];
|
||||
pa = SCALE(-pa, 15);
|
||||
CLIP_INT16(pa);
|
||||
pcm[(uint32_t)(1 << strideShift)] = (OI_INT16)(pa);
|
||||
pb = SCALE(-pb, 15);
|
||||
CLIP_INT16(pb);
|
||||
pcm[(uint32_t)(3 << strideShift)] = (OI_INT16)(pb);
|
||||
|
||||
|
||||
pa = dec_window_4[2] * (/*buffer[ 2] + */ buffer[78]); /* buffer[ 2] is always zero */
|
||||
pa += dec_window_4[6] * (buffer[14] /* + buffer[66]*/); /* buffer[66] is always zero */
|
||||
pa += dec_window_4[10] * (/*buffer[18] + */ buffer[62]); /* buffer[18] is always zero */
|
||||
pa += dec_window_4[14] * (buffer[30] /* + buffer[50]*/); /* buffer[50] is always zero */
|
||||
pa += dec_window_4[18] * (/*buffer[34] + */ buffer[46]); /* buffer[34] is always zero */
|
||||
pa = SCALE(-pa, 15);
|
||||
CLIP_INT16(pa);
|
||||
pcm[(uint32_t)(2 << strideShift)] = (OI_INT16)(pa);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
This routine implements the cosine modulation matrix for 4-subband
|
||||
synthesis. This is called "matrixing" in the SBC specification. This
|
||||
matrix, M4, can be factored into an 8-point Type II Discrete Cosine
|
||||
Transform, DCTII_4 and a matrix S4, given here:
|
||||
|
||||
@code
|
||||
__ __
|
||||
| 0 0 1 0 |
|
||||
| 0 0 0 1 |
|
||||
| 0 0 0 0 |
|
||||
| 0 0 0 -1 |
|
||||
S4 = | 0 0 -1 0 |
|
||||
| 0 -1 0 0 |
|
||||
| -1 0 0 0 |
|
||||
|__ 0 -1 0 0 __|
|
||||
|
||||
M4 * in = S4 * (DCTII_4 * in)
|
||||
@endcode
|
||||
|
||||
(DCTII_4 * in) is computed using a Fast Cosine Transform. The algorithm
|
||||
here is based on an implementation computed by the SPIRAL computer
|
||||
algebra system, manually converted to fixed-point arithmetic. S4 can be
|
||||
implemented using only assignment and negation.
|
||||
*/
|
||||
PRIVATE void cosineModulateSynth4(SBC_BUFFER_T * RESTRICT out, OI_INT32 const * RESTRICT in)
|
||||
{
|
||||
OI_INT32 f0, f1, f2, f3, f4, f7, f8, f9, f10;
|
||||
OI_INT32 y0, y1, y2, y3;
|
||||
|
||||
f0 = (in[0] - in[3]);
|
||||
f1 = (in[0] + in[3]);
|
||||
f2 = (in[1] - in[2]);
|
||||
f3 = (in[1] + in[2]);
|
||||
|
||||
f4 = f1 - f3;
|
||||
|
||||
y0 = -SCALE(f1 + f3, DCT_SHIFT);
|
||||
y2 = -SCALE(LONG_MULT_DCT(DCTII_4_K06_FIX, f4), DCT_SHIFT);
|
||||
f7 = f0 + f2;
|
||||
f8 = LONG_MULT_DCT(DCTII_4_K08_FIX, f0);
|
||||
f9 = LONG_MULT_DCT(DCTII_4_K09_FIX, f7);
|
||||
f10 = LONG_MULT_DCT(DCTII_4_K10_FIX, f2);
|
||||
y3 = -SCALE(f8 + f9, DCT_SHIFT);
|
||||
y1 = -SCALE(f10 - f9, DCT_SHIFT);
|
||||
|
||||
out[0] = (OI_INT16)-y2;
|
||||
out[1] = (OI_INT16)-y3;
|
||||
out[2] = (OI_INT16)0;
|
||||
out[3] = (OI_INT16)y3;
|
||||
out[4] = (OI_INT16)y2;
|
||||
out[5] = (OI_INT16)y1;
|
||||
out[6] = (OI_INT16)y0;
|
||||
out[7] = (OI_INT16)y1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
Reference in New Issue
Block a user