843 lines
25 KiB
C++
843 lines
25 KiB
C++
/* $Header: /cvs/root/winamp/vlb/channel.cpp,v 1.1 2009/04/28 20:21:08 audiodsp Exp $ */
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* Copyright 2000-2002 Dolby Laboratories, Inc. All Rights
|
|
* Reserved. Do not copy. Do not distribute.
|
|
* Confidential information.
|
|
*
|
|
* (C) copyright Fraunhofer - IIS (1998)
|
|
* All Rights Reserved
|
|
*
|
|
* filename: channel.cpp
|
|
* project : MPEG-2 AAC Decoder
|
|
* contents/description: independent channel stream object
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#include <new.h> // displacement new
|
|
|
|
#include "channel.h"
|
|
#include "bitstream.h"
|
|
#include "bitsequence.h"
|
|
|
|
// // // Base class for CSingleChannel, CChannelPair
|
|
|
|
CChannelElement::CChannelElement (CDolbyBitStream &bs)
|
|
: m_bs (bs),
|
|
m_GlobalGain (8)
|
|
{
|
|
m_WantEqualizer = false ;
|
|
m_WantSpectralData = false ;
|
|
}
|
|
|
|
CChannelElement::~CChannelElement ()
|
|
{}
|
|
|
|
CBlock *CChannelElement::ReadICS (CChannelInfo &ics_info, unsigned long *memory)
|
|
{
|
|
// allocates and reads one individual_channel_stream
|
|
|
|
m_GlobalGain.Read (m_bs) ;
|
|
|
|
if (!ics_info.IsValid ())
|
|
{
|
|
ics_info.Read (m_bs) ;
|
|
}
|
|
|
|
CBlock *block ;
|
|
|
|
if (ics_info.IsLongBlock ())
|
|
{
|
|
block = new (static_cast<void *>(memory)) CLongBlock (ics_info) ;
|
|
}
|
|
else
|
|
{
|
|
block = new (static_cast<void *>(memory)) CShortBlock (ics_info) ;
|
|
}
|
|
|
|
block->Read (m_bs, m_GlobalGain) ;
|
|
|
|
// 'block' points to the same location as 'memory', but keeping the
|
|
// m_Block pointers as class members saves us a lot of typecasting
|
|
|
|
return block ;
|
|
}
|
|
|
|
void CChannelElement::SetEqualization (bool wantEQ, float Mask [])
|
|
{
|
|
m_WantEqualizer = wantEQ ;
|
|
|
|
if (wantEQ)
|
|
{
|
|
for (int i = 0 ; i < CBlock::EqualizationMaskLength ; i++)
|
|
{
|
|
m_EqualizerMask [i] = Mask [i] ;
|
|
}
|
|
}
|
|
}
|
|
|
|
// // // CSingleChannel wraps single_channel_element()
|
|
|
|
CSingleChannel::CSingleChannel (CDolbyBitStream &theBitstream)
|
|
: CChannelElement (theBitstream)
|
|
{
|
|
|
|
#ifdef MAIN_PROFILE
|
|
m_Prediction.FullReset () ;
|
|
#endif
|
|
|
|
poIMDCTObject=new IMDCTObject(4096,256);
|
|
poNormalIMDCTObject=new IMDCTObject(2048,128);
|
|
ppfData=new float*[1];
|
|
ppshData=new short*[1];
|
|
|
|
m_poAudioDSP = new AudioIODSP(1);
|
|
}
|
|
|
|
CSingleChannel::~CSingleChannel ()
|
|
{
|
|
delete poIMDCTObject;
|
|
delete poNormalIMDCTObject;
|
|
delete[] ppfData;
|
|
delete[] ppshData;
|
|
delete m_poAudioDSP;
|
|
}
|
|
|
|
void CSingleChannel::Read (const CStreamInfo &si)
|
|
{
|
|
m_IcsInfo.Reset (si) ;
|
|
|
|
m_Block = ReadICS (m_IcsInfo, m_BlockMemory) ;
|
|
}
|
|
|
|
void CSingleChannel::Decode (AudioIOControl *poAudioIO, CStreamInfo &info, int stride /* = 1 */)
|
|
{
|
|
|
|
if (m_IcsInfo.IsMainProfile ())
|
|
{
|
|
#ifdef MAIN_PROFILE
|
|
m_Prediction.Apply (m_IcsInfo, *m_Block) ;
|
|
#else
|
|
throw EIllegalProfile();
|
|
#endif
|
|
}
|
|
|
|
|
|
m_Block->ApplyTools () ;
|
|
|
|
if (m_WantEqualizer)
|
|
{
|
|
m_Block->ApplyEqualizationMask (m_EqualizerMask) ;
|
|
}
|
|
m_Block->FrequencyToTime_Fast (m_Previous) ;
|
|
ppfData[0]=m_Block->AccessOutput();
|
|
|
|
// ApplyLimiter is called to minimize distortion from clipping. The limiter can only
|
|
// be applied to 128 samples at a time so there must be several calls to limit all 1024
|
|
// samples in a block. This is due to the way the buffer allocation is done in the
|
|
// constructor of the AudioIODSP class.
|
|
int iLength = 1024;
|
|
int grpIndex = 0;
|
|
short sThisLength = (iLength < DLYBUFSZ) ? iLength : DLYBUFSZ;
|
|
|
|
m_poAudioDSP->SetSamplingRate(info.GetSamplingRate());
|
|
for (int sSamp = 0; sSamp < iLength; sSamp += sThisLength)
|
|
{
|
|
m_poAudioDSP->ApplyLimiter(ppfData, sThisLength, grpIndex);
|
|
sThisLength = ((iLength - sThisLength * grpIndex++) < DLYBUFSZ) ?
|
|
(iLength - sThisLength * grpIndex++) : DLYBUFSZ;
|
|
}
|
|
poAudioIO->IO(ppfData,1024);
|
|
m_Block->~CBlock () ;
|
|
}
|
|
|
|
void CSingleChannel::DecodeDolby ( AudioIOControl *poAudioIO,
|
|
DOLBY_PAYLOAD_STRUCT *psDSEInfo,
|
|
CStreamInfo &info,
|
|
int stride /* = 1 */)
|
|
{
|
|
int iWindowType;
|
|
int iWindowShape;
|
|
int i,j,grp_index,win_in_grp_index;
|
|
int startingBinForThisWindow, startingBaseBandBin, halfWindowLength;
|
|
int overall_window_number1, overall_window_number2;
|
|
int firstWinThisGrp;
|
|
float avgCopyEnergies[8][SE_MAX_NUM_BANDS];
|
|
int iBandCounter;
|
|
|
|
// previous window sequence
|
|
// need to keep this variable around to handle legacy code which forces
|
|
// spectral extend to rely on a special case for WindowSequence == 3
|
|
|
|
static int previousWindowSequence = 0;
|
|
|
|
// get pointer to spectral data
|
|
// these calls really need to depend on the blocktype -
|
|
// short blocks require the window number (0-7) to be passed
|
|
// to AccessSpectralData(); long blocks do not require this argument.
|
|
|
|
ppfData[0]=m_Block->AccessSpectralData();
|
|
|
|
// Apply DNS here.
|
|
// Only apply DNS if we have valid DNS information read from the SE bitstream.
|
|
// If there was a SE bitstream reading error of *any* kind, do not apply DNS.
|
|
|
|
if (psDSEInfo->iDolbyBitStreamWarning == 0)
|
|
{
|
|
// applyDNS() applies to all window groups at the same time, so there is
|
|
// no need to call applyDNS() for each window group individually.
|
|
applyDNS(ppfData[0],&psDSEInfo->asDNSInfoStruct[0],previousWindowSequence);
|
|
}
|
|
|
|
#ifdef MAIN_PROFILE
|
|
if (m_IcsInfo.IsMainProfile ())
|
|
{
|
|
m_Prediction.Apply (m_IcsInfo, *m_Block) ;
|
|
}
|
|
#endif
|
|
|
|
iWindowType=m_IcsInfo.GetWindowSequence();
|
|
iWindowShape=m_IcsInfo.GetWindowShape();
|
|
|
|
//Do TNS:
|
|
m_Block->ApplyTools () ;
|
|
|
|
// Spectral Extension.
|
|
|
|
// for each window group...
|
|
overall_window_number1 = 0;
|
|
overall_window_number2 = 0;
|
|
|
|
for(grp_index=0;grp_index<psDSEInfo->iGroupCount[0];grp_index++)
|
|
{
|
|
firstWinThisGrp = overall_window_number1;
|
|
|
|
// for each window within the current window group...
|
|
for(win_in_grp_index=0;
|
|
win_in_grp_index<psDSEInfo->iGroupLength[0][grp_index];
|
|
win_in_grp_index++,overall_window_number1++)
|
|
{
|
|
|
|
// compute some parameters depedent on short/long blocktype, window group number, and transform length
|
|
if (psDSEInfo->asDNSInfoStruct[0].iWindowSequence == 2)
|
|
{
|
|
// SHORT BLOCKS
|
|
startingBinForThisWindow = overall_window_number1*256;
|
|
startingBaseBandBin = 12;
|
|
halfWindowLength = (psDSEInfo->iUsesDoubleLengthXForm) ? 256 : 128;
|
|
}
|
|
else
|
|
{
|
|
// LONG, STOP, START blocks
|
|
startingBinForThisWindow = 0;
|
|
#ifdef NEW_BUFFER_MODEL
|
|
startingBaseBandBin = 96;
|
|
#else
|
|
startingBaseBandBin = 100;
|
|
#endif
|
|
halfWindowLength = (psDSEInfo->iUsesDoubleLengthXForm) ? 2048 : 1024;
|
|
}
|
|
|
|
computeAvgCopyEnergies(&ppfData[0][startingBinForThisWindow],
|
|
startingBaseBandBin,
|
|
psDSEInfo->aiCopyStop[0],
|
|
psDSEInfo->num_se_bands[0],
|
|
psDSEInfo->seBands[0][grp_index],
|
|
halfWindowLength,
|
|
psDSEInfo->asDNSInfoStruct[0].iWindowSequence,
|
|
avgCopyEnergies[overall_window_number1]);
|
|
}/* win_in_grp_index */
|
|
|
|
/* compute average energies for each group before passing into spectral extend */
|
|
// accumulate energies for this group
|
|
|
|
for(win_in_grp_index=1;
|
|
win_in_grp_index<psDSEInfo->iGroupLength[0][grp_index];
|
|
win_in_grp_index++)
|
|
{
|
|
for(iBandCounter=0;iBandCounter<psDSEInfo->num_se_bands[0];iBandCounter++)
|
|
{
|
|
avgCopyEnergies[firstWinThisGrp][iBandCounter] += avgCopyEnergies[firstWinThisGrp + win_in_grp_index][iBandCounter];
|
|
}
|
|
}
|
|
|
|
// find the average energy for this group
|
|
for(iBandCounter=0;iBandCounter<psDSEInfo->num_se_bands[0];iBandCounter++)
|
|
{
|
|
avgCopyEnergies[firstWinThisGrp][iBandCounter] /= psDSEInfo->iGroupLength[0][grp_index];
|
|
}
|
|
|
|
// copy this average energy to all values in the avgCopyEnergies array which correspond to windows in the current group
|
|
for(win_in_grp_index=1;
|
|
win_in_grp_index<psDSEInfo->iGroupLength[0][grp_index];
|
|
win_in_grp_index++)
|
|
{
|
|
for(iBandCounter=0;iBandCounter<psDSEInfo->num_se_bands[0];iBandCounter++)
|
|
{
|
|
avgCopyEnergies[firstWinThisGrp + win_in_grp_index][iBandCounter] = avgCopyEnergies[firstWinThisGrp][iBandCounter];
|
|
}
|
|
}
|
|
|
|
// for each window within the current window group...
|
|
for (win_in_grp_index=0;
|
|
win_in_grp_index<psDSEInfo->iGroupLength[0][grp_index];
|
|
win_in_grp_index++,overall_window_number2++)
|
|
{
|
|
firstWinThisGrp = overall_window_number2;
|
|
|
|
// check to see if dolby bitstream was read correctly.
|
|
// If so, spectral extend all windows, whether we're dealing with a long block or short block
|
|
if (psDSEInfo->iDolbyBitStreamWarning == 0)
|
|
{
|
|
// compute some parameters dependent short/long blocktype, window group number, and transform length
|
|
if (psDSEInfo->asDNSInfoStruct[0].iWindowSequence == 2)
|
|
{
|
|
// SHORT blocks
|
|
// The constant 256 is ugly here - but is equal to (2 * CShortBlock::MaximumBins).
|
|
// This is the spacing of the mdct coefficients in the buffer ppfData[n], independent
|
|
// of whether we're using a single or double length transform.
|
|
startingBinForThisWindow = overall_window_number2*256;
|
|
startingBaseBandBin = 12;
|
|
halfWindowLength = (psDSEInfo->iUsesDoubleLengthXForm) ? 256 : 128;
|
|
}
|
|
else
|
|
{
|
|
// LONG, STOP, START blocks
|
|
startingBinForThisWindow = 0;
|
|
#ifdef NEW_BUFFER_MODEL
|
|
startingBaseBandBin = 96;
|
|
#else
|
|
startingBaseBandBin = 100;
|
|
#endif
|
|
halfWindowLength = (psDSEInfo->iUsesDoubleLengthXForm) ? 2048 : 1024;
|
|
}
|
|
|
|
spectralExtend(&ppfData[0][startingBinForThisWindow],
|
|
startingBaseBandBin,
|
|
psDSEInfo->aiCopyStop[0],
|
|
psDSEInfo->sfm[0][grp_index],
|
|
psDSEInfo->num_se_bands[0],
|
|
psDSEInfo->seBands[0][grp_index],
|
|
psDSEInfo->delta_power_values[0][grp_index],
|
|
psDSEInfo->fdamp[0][grp_index],
|
|
halfWindowLength,
|
|
psDSEInfo->asDNSInfoStruct[0].iWindowSequence,
|
|
psDSEInfo->iSEPowerResolution,
|
|
previousWindowSequence,
|
|
avgCopyEnergies[overall_window_number2]);
|
|
|
|
}
|
|
else
|
|
{
|
|
// zero out the extension band of each window if there was a bitstream error.
|
|
// be careful here- we may be dealing with many short blocks or a single long block
|
|
|
|
if (psDSEInfo->asDNSInfoStruct[0].iWindowSequence == 2)
|
|
{
|
|
// SHORT blocks
|
|
for(i=0;i<8;i++) {
|
|
for(j=psDSEInfo->aiCopyStop[0]; j<256; j++)
|
|
{
|
|
ppfData[0][i*256+j]=0.0f;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// LONG, STOP, START blocks
|
|
for (i = psDSEInfo->aiCopyStop[0]; i<2048; i++)
|
|
{
|
|
ppfData[0][i] = 0.0f;
|
|
}
|
|
}
|
|
|
|
} // if psDSEInfo->iDolbyBitStreamWarning...
|
|
|
|
} // for win_in_grp_index
|
|
|
|
} // for grp_index
|
|
|
|
/* Double all TCs if using a double length x-form so we do not lose 6dB
|
|
* after the imdct.
|
|
*/
|
|
|
|
if (psDSEInfo->iUsesDoubleLengthXForm)
|
|
{
|
|
for (i = 0; i < 2048; i++)
|
|
{
|
|
ppfData[0][i] *= 2.0f;
|
|
}
|
|
}
|
|
|
|
// do the transform
|
|
if (psDSEInfo->iUsesDoubleLengthXForm)
|
|
{
|
|
poIMDCTObject->Transform(ppfData[0],iWindowType,iWindowShape);
|
|
|
|
// The limiter can only be applied to 128 samples at a time
|
|
// so there must be several calls to limit all 2048 samples
|
|
// in a block. This is due to the way the buffer allocation
|
|
// is done in the constructor of the AudioIODSP class
|
|
int iLength = 2048;
|
|
int grpIndex = 0;
|
|
int sThisLength = (iLength < DLYBUFSZ) ? iLength : DLYBUFSZ;
|
|
|
|
m_poAudioDSP->SetSamplingRate(info.GetSamplingRate());
|
|
for (int sSamp = 0; sSamp < iLength; sSamp += sThisLength)
|
|
{
|
|
// Limiter is applied to ppfData
|
|
m_poAudioDSP->ApplyLimiter(ppfData, sThisLength, grpIndex);
|
|
sThisLength = ((iLength - sThisLength * grpIndex++) < DLYBUFSZ) ?
|
|
(iLength - sThisLength * grpIndex++) : DLYBUFSZ;
|
|
}
|
|
|
|
poAudioIO->IO(ppfData,2048);
|
|
}
|
|
else
|
|
{
|
|
poNormalIMDCTObject->Transform(ppfData[0],iWindowType,iWindowShape);
|
|
|
|
// Apply Limiter so that distortion from clipping is minimized
|
|
|
|
// The limiter can only be applied to 128 samples at a time
|
|
// so there must be several calls to limit all 2048 samples
|
|
// in a block. This is due to the way the buffer allocation
|
|
// is done in the constructor of the AudioIODSP class
|
|
int iLength = 1024;
|
|
int grpIndex = 0;
|
|
short sThisLength = (iLength < DLYBUFSZ) ? iLength : DLYBUFSZ;
|
|
|
|
m_poAudioDSP->SetSamplingRate(info.GetSamplingRate());
|
|
for (int sSamp = 0; sSamp < iLength; sSamp += sThisLength)
|
|
{
|
|
// Limiter is applied to ppfData
|
|
m_poAudioDSP->ApplyLimiter(ppfData, sThisLength, grpIndex);
|
|
sThisLength = ((iLength - sThisLength * grpIndex++) < DLYBUFSZ) ?
|
|
(iLength - sThisLength * grpIndex++) : DLYBUFSZ;
|
|
}
|
|
|
|
poAudioIO->IO(ppfData,1024);
|
|
}
|
|
|
|
// update previousWindowSequence
|
|
previousWindowSequence = psDSEInfo->asDNSInfoStruct[0].iWindowSequence;
|
|
|
|
m_Block->~CBlock () ;
|
|
}
|
|
|
|
// // // CChannelPair wraps channel_pair_element()
|
|
|
|
CChannelPair::CChannelPair (CDolbyBitStream &theBitstream)
|
|
: CChannelElement (theBitstream),
|
|
m_CommonWindow (1)
|
|
{
|
|
|
|
#ifdef MAIN_PROFILE
|
|
m_Prediction [L].FullReset () ;
|
|
m_Prediction [R].FullReset () ;
|
|
#endif
|
|
|
|
ppoIMDCTObject = new IMDCTObject* [2];
|
|
ppoNormalIMDCTObject = new IMDCTObject* [2];
|
|
|
|
for (int n = 0; n < 2; n++)
|
|
{
|
|
ppoIMDCTObject[n] = new IMDCTObject(4096,256);
|
|
ppoNormalIMDCTObject[n] = new IMDCTObject(2048,128);
|
|
}
|
|
|
|
ppfData = new float*[2];
|
|
ppshData = new short*[2];
|
|
|
|
m_poAudioDSP = new AudioIODSP(2);
|
|
}
|
|
|
|
CChannelPair::~CChannelPair ()
|
|
{
|
|
for (int n = 0; n < 2; n++)
|
|
{
|
|
delete ppoIMDCTObject[n];
|
|
delete ppoNormalIMDCTObject[n];
|
|
}
|
|
delete[] ppoIMDCTObject;
|
|
delete[] ppoNormalIMDCTObject;
|
|
|
|
delete[] ppfData;
|
|
delete[] ppshData;
|
|
delete m_poAudioDSP;
|
|
}
|
|
|
|
void CChannelPair::Read (const CStreamInfo &si)
|
|
{
|
|
m_IcsInfo [L].Reset (si) ;
|
|
m_IcsInfo [R].Reset (si) ;
|
|
|
|
if (m_CommonWindow.Read (m_bs))
|
|
{
|
|
m_IcsInfo [L].Read (m_bs) ;
|
|
m_IcsInfo [R] = m_IcsInfo [L] ;
|
|
|
|
m_JointStereo.Read (m_IcsInfo [L], m_bs) ;
|
|
}
|
|
|
|
m_Block [L] = ReadICS (m_IcsInfo [L], m_BlockMemory [L]) ;
|
|
m_bs.SetPositionMarker (CDolbyBitStream::SecondIndividualChannelStart) ;
|
|
m_Block [R] = ReadICS (m_IcsInfo [R], m_BlockMemory [R]) ;
|
|
}
|
|
|
|
void CChannelPair::Decode (AudioIOControl *poAudioIO, CStreamInfo &info, int stride /* = 1 */)
|
|
{
|
|
|
|
#ifdef MAIN_PROFILE
|
|
// - apply prediction tool to left (coded) channel
|
|
// - calculate right channel from intensity position
|
|
// - apply prediction tool to right channel
|
|
|
|
// unfortunately this breaks m_JointStereo::Apply() into two steps
|
|
#endif
|
|
|
|
if (m_CommonWindow)
|
|
{
|
|
m_JointStereo.ApplyMS (m_IcsInfo [L], *m_Block [L], *m_Block [R]) ;
|
|
}
|
|
|
|
#ifdef MAIN_PROFILE
|
|
if (m_IcsInfo [L].IsMainProfile ())
|
|
{
|
|
m_Prediction [L].Apply (m_IcsInfo [L], *m_Block [L]) ;
|
|
}
|
|
#endif
|
|
|
|
if (m_CommonWindow)
|
|
{
|
|
m_JointStereo.ApplyIS (m_IcsInfo [L], *m_Block [L], *m_Block [R]) ;
|
|
}
|
|
|
|
#ifdef MAIN_PROFILE
|
|
if (m_IcsInfo [R].IsMainProfile ())
|
|
{
|
|
m_Prediction [R].Apply (m_IcsInfo [R], *m_Block [R]) ;
|
|
}
|
|
#endif
|
|
|
|
for (int channel = 0 ; channel < Channels ; channel++)
|
|
{
|
|
m_Block [channel]->ApplyTools () ;
|
|
|
|
if (m_WantEqualizer)
|
|
{
|
|
m_Block [channel]->ApplyEqualizationMask (m_EqualizerMask) ;
|
|
}
|
|
m_Block [channel]->FrequencyToTime_Fast (m_Previous [channel]) ;
|
|
ppfData[channel]=m_Block[channel]->AccessOutput();
|
|
}
|
|
|
|
// ApplyLimiter is called to minimize distortion from clipping. The limiter can only
|
|
// be applied to 128 samples at a time so there must be several calls to limit all
|
|
// samples in a block. This is due to the way the buffer allocation is done in the
|
|
// constructor of the AudioIODSP class.0
|
|
int iLength = 1024;
|
|
int grpIndex = 0;
|
|
int sThisLength = (iLength < DLYBUFSZ) ? iLength : DLYBUFSZ;
|
|
|
|
m_poAudioDSP->SetSamplingRate(info.GetSamplingRate());
|
|
for (int sSamp = 0; sSamp < iLength; sSamp += sThisLength)
|
|
{
|
|
m_poAudioDSP->ApplyLimiter(ppfData, sThisLength, grpIndex);
|
|
sThisLength = ((iLength - sThisLength * grpIndex++) < DLYBUFSZ) ?
|
|
(iLength - sThisLength * grpIndex++) : DLYBUFSZ;
|
|
}
|
|
poAudioIO->IO(ppfData,1024);
|
|
m_Block [L]->~CBlock () ;
|
|
m_Block [R]->~CBlock () ;
|
|
}
|
|
|
|
void CChannelPair::DecodeDolby (AudioIOControl *poAudioIO,
|
|
DOLBY_PAYLOAD_STRUCT *psDSEInfo,
|
|
CStreamInfo &info,
|
|
int stride /* = 1 */)
|
|
{
|
|
int channel, win_in_grp_index;
|
|
int iWindowType;
|
|
int iWindowShape;
|
|
int i,j,grp_index;
|
|
|
|
int startingBinForThisWindow;
|
|
int startingBaseBandBin;
|
|
int halfWindowLength;
|
|
int overall_window_number1, overall_window_number2;
|
|
float avgCopyEnergies[8][SE_MAX_NUM_BANDS];
|
|
int iBandCounter;
|
|
int firstWinThisGrp;
|
|
|
|
// need to keep this variable around to handle legacy code which forces
|
|
// spectral extend to rely on a special case for WindowSequence == 3
|
|
|
|
static int previousWindowSequence[2] = {0,0};
|
|
|
|
// apply DNS to each channel
|
|
// these calls really need to depend on the blocktype -
|
|
// short blocks require the window number (0-7) to be passed
|
|
// to AccessSpectralData(); long blocks do not require this argument.
|
|
ppfData[L]=m_Block[L]->AccessSpectralData();
|
|
ppfData[R]=m_Block[R]->AccessSpectralData();
|
|
|
|
// Apply DNS here
|
|
// Only apply DNS if we have valid DNS information read from the SE bitstream.
|
|
// If there was a SE bitstream reading error of *any* kind, do not apply DNS
|
|
|
|
|
|
if (psDSEInfo->iDolbyBitStreamWarning == 0)
|
|
{
|
|
// applyDNS() applies to all window groups at the same time, so there is
|
|
// no need to call applyDNS() for each window group individually.
|
|
applyDNS(ppfData[L],&psDSEInfo->asDNSInfoStruct[L],previousWindowSequence[L]);
|
|
applyDNS(ppfData[R],&psDSEInfo->asDNSInfoStruct[R],previousWindowSequence[R]);
|
|
}
|
|
|
|
|
|
#ifdef MAIN_PROFILE
|
|
// - apply prediction tool to left (coded) channel
|
|
// - calculate right channel from intensity position
|
|
// - apply prediction tool to right channel
|
|
|
|
// unfortunately this breaks m_JointStereo::Apply() into two steps
|
|
#endif
|
|
|
|
if (m_CommonWindow)
|
|
{
|
|
m_JointStereo.ApplyMS (m_IcsInfo [L], *m_Block [L], *m_Block [R]) ;
|
|
}
|
|
|
|
#ifdef MAIN_PROFILE
|
|
if (m_IcsInfo [L].IsMainProfile ())
|
|
{
|
|
m_Prediction [L].Apply (m_IcsInfo [L], *m_Block [L]) ;
|
|
}
|
|
#endif
|
|
|
|
if (m_CommonWindow)
|
|
{
|
|
m_JointStereo.ApplyIS (m_IcsInfo [L], *m_Block [L], *m_Block [R]) ;
|
|
}
|
|
|
|
#ifdef MAIN_PROFILE
|
|
if (m_IcsInfo [R].IsMainProfile ())
|
|
{
|
|
m_Prediction [R].Apply (m_IcsInfo [R], *m_Block [R]) ;
|
|
}
|
|
#endif
|
|
|
|
for (channel = 0 ; channel < Channels ; channel++)
|
|
{
|
|
iWindowType=m_IcsInfo[channel].GetWindowSequence();
|
|
iWindowShape=m_IcsInfo[channel].GetWindowShape();
|
|
ppfData[channel]=m_Block[channel]->AccessSpectralData();
|
|
|
|
// Apply TNS
|
|
m_Block [channel]->ApplyTools () ;
|
|
|
|
// Spectral Extension for Stereo Goes Here!
|
|
|
|
// for each window group...
|
|
overall_window_number1 = 0;
|
|
overall_window_number2 = 0;
|
|
|
|
for (grp_index=0;grp_index<psDSEInfo->iGroupCount[channel];grp_index++)
|
|
{
|
|
firstWinThisGrp = overall_window_number1;
|
|
|
|
// another window_in_group loop: compute average energy in this group for all bands in the current group
|
|
for (win_in_grp_index=0;
|
|
win_in_grp_index<psDSEInfo->iGroupLength[channel][grp_index];
|
|
win_in_grp_index++,overall_window_number1++)
|
|
{
|
|
|
|
// compute some parameters dependent short/long blocktype, window group number, and transform length
|
|
if (psDSEInfo->asDNSInfoStruct[channel].iWindowSequence == 2)
|
|
{
|
|
// SHORT blocks
|
|
// The constant 256 is ugly here - but is equal to (2 * CShortBlock::MaximumBins).
|
|
// This is the spacing of the mdct coefficients in the buffer ppfData[n], independent
|
|
// of whether we're using a single or double length transform.
|
|
startingBinForThisWindow = overall_window_number1*256;
|
|
startingBaseBandBin = 12;
|
|
halfWindowLength = (psDSEInfo->iUsesDoubleLengthXForm) ? 256 : 128;
|
|
}
|
|
else
|
|
{
|
|
// LONG, STOP, START blocks
|
|
startingBinForThisWindow = 0;
|
|
#ifdef NEW_BUFFER_MODEL
|
|
startingBaseBandBin = 96;
|
|
#else
|
|
startingBaseBandBin = 100;
|
|
#endif
|
|
halfWindowLength = (psDSEInfo->iUsesDoubleLengthXForm) ? 2048 : 1024;
|
|
}
|
|
|
|
computeAvgCopyEnergies(&ppfData[channel][startingBinForThisWindow],
|
|
startingBaseBandBin,
|
|
psDSEInfo->aiCopyStop[channel],
|
|
psDSEInfo->num_se_bands[channel],
|
|
psDSEInfo->seBands[channel][grp_index],
|
|
halfWindowLength,
|
|
psDSEInfo->asDNSInfoStruct[channel].iWindowSequence,
|
|
avgCopyEnergies[overall_window_number1]);
|
|
|
|
} /* win_in_grp_index */
|
|
|
|
/* compute average energies for each group before passing into spectral extend */
|
|
// accumulate energies for this group
|
|
|
|
for(win_in_grp_index=1;
|
|
win_in_grp_index<psDSEInfo->iGroupLength[channel][grp_index];
|
|
win_in_grp_index++)
|
|
{
|
|
for(iBandCounter=0;iBandCounter<psDSEInfo->num_se_bands[channel];iBandCounter++)
|
|
{
|
|
avgCopyEnergies[firstWinThisGrp][iBandCounter] += avgCopyEnergies[firstWinThisGrp + win_in_grp_index][iBandCounter];
|
|
}
|
|
}
|
|
|
|
// find the average energy for this group
|
|
for(iBandCounter=0;iBandCounter<psDSEInfo->num_se_bands[channel];iBandCounter++)
|
|
{
|
|
avgCopyEnergies[firstWinThisGrp][iBandCounter] /= psDSEInfo->iGroupLength[channel][grp_index];
|
|
}
|
|
|
|
// copy this average energy to all values in the avgCopyEnergies array which correspond to windows in the current group
|
|
for(win_in_grp_index=1;
|
|
win_in_grp_index<psDSEInfo->iGroupLength[channel][grp_index];
|
|
win_in_grp_index++)
|
|
{
|
|
for(iBandCounter=0;iBandCounter<psDSEInfo->num_se_bands[channel];iBandCounter++)
|
|
{
|
|
avgCopyEnergies[firstWinThisGrp + win_in_grp_index][iBandCounter] = avgCopyEnergies[firstWinThisGrp][iBandCounter];
|
|
}
|
|
}
|
|
|
|
// for each window within the current window group...
|
|
for (win_in_grp_index=0;
|
|
win_in_grp_index<psDSEInfo->iGroupLength[channel][grp_index];
|
|
win_in_grp_index++,overall_window_number2++)
|
|
{
|
|
firstWinThisGrp = overall_window_number2;
|
|
|
|
// check to see if dolby bitstream was read correctly.
|
|
// If so, spectral extend all windows, whether we're dealing with a long block or short block
|
|
if (psDSEInfo->iDolbyBitStreamWarning == 0)
|
|
{
|
|
// compute some parameters dependent short/long blocktype, window group number, and transform length
|
|
if (psDSEInfo->asDNSInfoStruct[channel].iWindowSequence == 2)
|
|
{
|
|
// SHORT blocks
|
|
// The constant 256 is ugly here - but is equal to (2 * CShortBlock::MaximumBins).
|
|
// This is the spacing of the mdct coefficients in the buffer ppfData[n], independent
|
|
// of whether we're using a single or double length transform.
|
|
startingBinForThisWindow = overall_window_number2*256;
|
|
startingBaseBandBin = 12;
|
|
halfWindowLength = (psDSEInfo->iUsesDoubleLengthXForm) ? 256 : 128;
|
|
}
|
|
else
|
|
{
|
|
// LONG, STOP, START blocks
|
|
startingBinForThisWindow = 0;
|
|
#ifdef NEW_BUFFER_MODEL
|
|
startingBaseBandBin = 96;
|
|
#else
|
|
startingBaseBandBin = 100;
|
|
#endif
|
|
halfWindowLength = (psDSEInfo->iUsesDoubleLengthXForm) ? 2048 : 1024;
|
|
}
|
|
|
|
spectralExtend (&ppfData[channel][startingBinForThisWindow],
|
|
startingBaseBandBin,
|
|
psDSEInfo->aiCopyStop[channel],
|
|
psDSEInfo->sfm[channel][grp_index],
|
|
psDSEInfo->num_se_bands[channel],
|
|
psDSEInfo->seBands[channel][grp_index],
|
|
psDSEInfo->delta_power_values[channel][grp_index],
|
|
psDSEInfo->fdamp[channel][grp_index],
|
|
halfWindowLength,
|
|
psDSEInfo->asDNSInfoStruct[channel].iWindowSequence,
|
|
psDSEInfo->iSEPowerResolution,
|
|
previousWindowSequence[channel],
|
|
avgCopyEnergies[overall_window_number2]);
|
|
|
|
}
|
|
else
|
|
{
|
|
// zero out the extension band of each window if there was a bitstream error.
|
|
// be careful here- we may be dealing with many short blocks or a single long block
|
|
|
|
if (psDSEInfo->asDNSInfoStruct[channel].iWindowSequence == 2)
|
|
{
|
|
// SHORT blocks
|
|
for(i=0;i<8;i++) {
|
|
for(j=psDSEInfo->aiCopyStop[channel]; j<256; j++)
|
|
{
|
|
ppfData[channel][i*256+j]=0.0f;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// LONG, STOP, START blocks
|
|
for (i = psDSEInfo->aiCopyStop[channel]; i<2048; i++)
|
|
{
|
|
ppfData[channel][i] = 0.0f;
|
|
}
|
|
}
|
|
|
|
} // if psDSEInfo->iDolbyBitStreamWarning...
|
|
|
|
} // for win_in_grp_index...
|
|
|
|
} // for grp_index...
|
|
|
|
/* Double all TCs if using a double length x-form so we do not lose 6dB
|
|
* after the imdct.
|
|
*/
|
|
if (psDSEInfo->iUsesDoubleLengthXForm)
|
|
{
|
|
for (i = 0; i < 2048; i++)
|
|
{
|
|
ppfData[channel][i] *= 2.0f;
|
|
}
|
|
}
|
|
|
|
// do the transform
|
|
if (psDSEInfo->iUsesDoubleLengthXForm)
|
|
{
|
|
ppoIMDCTObject[channel]->Transform(ppfData[channel],iWindowType,iWindowShape);
|
|
}
|
|
else
|
|
{
|
|
ppoNormalIMDCTObject[channel]->Transform(ppfData[channel],iWindowType,iWindowShape);
|
|
}
|
|
|
|
} // for channel
|
|
|
|
int iLength = (psDSEInfo->iUsesDoubleLengthXForm) ? 2048 : 1024;
|
|
int grpIndex = 0;
|
|
int sThisLength = (iLength < DLYBUFSZ) ? iLength : DLYBUFSZ;
|
|
|
|
m_poAudioDSP->SetSamplingRate(info.GetSamplingRate());
|
|
for (int sSamp = 0; sSamp < iLength; sSamp += sThisLength)
|
|
{
|
|
m_poAudioDSP->ApplyLimiter(ppfData, sThisLength, grpIndex);
|
|
sThisLength = ((iLength - sThisLength * grpIndex++) < DLYBUFSZ) ?
|
|
(iLength - sThisLength * grpIndex++) : DLYBUFSZ;
|
|
}
|
|
|
|
poAudioIO->IO(ppfData, iLength);
|
|
|
|
// update previousWindowSequence
|
|
previousWindowSequence[L] = psDSEInfo->asDNSInfoStruct[L].iWindowSequence;
|
|
previousWindowSequence[R] = psDSEInfo->asDNSInfoStruct[R].iWindowSequence;
|
|
|
|
m_Block [L]->~CBlock () ;
|
|
m_Block [R]->~CBlock () ;
|
|
} |