Reconstructing AAC CPE

From MultimediaWiki
Jump to navigation Jump to search

Part of Understanding AAC

This page describes the process of reconstructing PCM data based on the decoded CPE parameters in an AAC bitstream. For the moment, this description focuses on what is necessary to reconstruct low complexity (LC) profile data.

Reconstruction Process

 specrec.c:reconstruct_channel_pair()
   +-specrec.c:allocate_channel_pair()
   +-specrec.c:inverse_quantization()
   +-specrec.c:apply_scalefactors()
   +-specrec.c:quant_to_spec()
   +-pns.c:pns_decode()
   +-ms.c:ms_decode()
   +-is.c:is_decode()
   +-ic_predict.c:ic_prediction() (main profile)
   +-ic_predict.c:pns_reset_pred_state() (main profile)
   +-(processing that is specific LTP & LD profiles)
   +-tns.c:tns_decode_frame()
   +-drc.c:drc_decode()
   +-filtbank.c:ifilter_bank()
     +-filtbank.c:imdct_long()
     +-mdct.c:faad_imdct()
   +-ssr.c:ssr_decode()
   +-lt_predict.c:lt_update_state()
   +-sbr_dec.c:sbrDecodeInit()
   +-sbr_dec.c:sbrDecodeCoupleFrame()

reconstruct_channel_pair(2 ic_streams, 2 spec_data arrays(16 bit ints))

 declare 2 1024-element float arrays for spectral coefficients (spec_coeff1 and spec_coeff2)
 inverse_quantization(spec_coeff1, spec_data1)
 inverse_quantization(spec_coeff2, spec_data2)
 apply_scalefactors(ics1, spec_coeff1)
 apply_scalefactors(ics2, spec_coeff2)
 if ics1.window_sequence is EIGHT_SHORT_SEQUENCE (2)
   quant_to_spec(ics1, spec_coeff1)
 if ics2.window_sequence is EIGHT_SHORT_SEQUENCE (2)
   quant_to_spec(ics2, spec_coeff2)
 if ics1.ms_mask_present
   pns_decode(ics1, ics2, spec_coef1, spec_coeff2)
 else
   pns_decode(ics1, spec_coef1)
   pns_decode(ics2, spec_coef2)
 ms_decode(ics1, ics2, spec_coef1, spec_coef2)
 is_decode(ics1, ics2, spec_coef1, spec_coef2)
 // main profile decoding
 // LTP decoding
 tns_decode(ics1, spec_coeff1)
 tns_decode(ics2, spec_coeff2)
 // DRC stuff
 ifilter_bank(ics1, spec_coeff1)
 ifilter_bank(ics2, spec_coeff2)
 save window shape for next frame (I thought frames were independent)
 // LTP stuff
 // SBR stuff

allocate_channel_pair()

This function mostly contains considerations for non-LC decoding modes. Need to revisit the mechanics of this later.

inverse_quantization(real spec_coeff[1024], int16 spec_data[1024])

Perform inverse non-linear quantization of spectral coefficients, converting from int -> real in the process. Can this be effectively parallelized via SIMD?

 foreach i in 0..1023
   spec_coeff[i] = -(abs(spec_data[i])4/3)

apply_scalefactors(ic_string ics, real spec_coeff[1024])

 nshort = frame_length (1024) / 8
 groups = 0
 foreach g in 0..ics.num_window_groups - 1
   k = 0
   foreach scalefactor_band in 0..ics.max_scalefactor_bands - 1
     top = ics.section_scalefactor_band_offset[g][scalefactor_band + 1]
     if (ics.scalefactors[g][scalefactor_band] < 0) OR (ics.scalefactors[g][scalefactor_band] > 255)
       exponent = fraction = 0
     else
       exponent = ics.scalefactors[g][scalefactor_band] >> 2
       fraction = ics.scalefactors[g][scalefactor_band] & 3
     while (k < top)
       spec_coeff[k + (groups * nshort) + 0] *= pow2sf_tab[exponent]
       spec_coeff[k + (groups * nshort) + 0] *= pow2_table[fraction]
       spec_coeff[k + (groups * nshort) + 1] *= pow2sf_tab[exponent]
       spec_coeff[k + (groups * nshort) + 1] *= pow2_table[fraction]
       spec_coeff[k + (groups * nshort) + 2] *= pow2sf_tab[exponent]
       spec_coeff[k + (groups * nshort) + 2] *= pow2_table[fraction]
       spec_coeff[k + (groups * nshort) + 3] *= pow2sf_tab[exponent]
       spec_coeff[k + (groups * nshort) + 3] *= pow2_table[fraction]
       k += 4
   groups += ics.window_group_length[g]

pow2sf_tab[] is defined as:

 foreach i in -25..38
   pow2sf_tab[i] = 2i

pow2_table[] is defined as:

   pow2_table[i] = 2i/4

quant_to_spec(ic_stream ics, real spec_coeff[1024])

Comment from FAAD2:

 For ONLY_LONG_SEQUENCE windows (num_window_groups = 1,
 window_group_length[0] = 1) the spectral data is in ascending spectral
 order.
 For the EIGHT_SHORT_SEQUENCE window, the spectral order depends on the
 grouping in the following manner:
 - Groups are ordered sequentially
 - Within a group, a scalefactor band consists of the spectral data of all
   grouped SHORT_WINDOWs for the associated scalefactor window band. To
   clarify via example, the length of a group is in the range of one to eight
   SHORT_WINDOWs.
 - If there are eight groups each with length one (num_window_groups = 8,
   window_group_length[0..7] = 1), the result is a sequence of eight spectra,
   each in ascending spectral order.
 - If there is only one group with length eight (num_window_groups = 1,
   window_group_length[0] = 8), the result is that spectral data of all eight
   SHORT_WINDOWs is interleaved by scalefactor window bands.
 - Within a scalefactor window band, the coefficients are in ascending
   spectral order.

Algorithm:

 allocate a temporary spectral data array: temp_spec[1024]
 k = gindex = 0
 foreach g in 0..ics.num_window_groups - 1
   j = gincrease = 0
   window_increment = ics.scalefactor_window_band_offset[ics.num_scalefactor_window_bands]
   foreach scalefactor_band in 0..ics.num_scalefactor_window_bands - 1 
     width = ics.scalefactor_window_band_offset[scalefactor_band + 1] -
      ics.scalefactor_window_band[scalefactor_band]
     foreach window in 0..ics.window_group_length[g] - 1
       foreach bin in 0..width - 1, step 4
         temp_spec[gindex + (window * window_increment) + j + bin + 0] = spec_coeff[k + 0]
         temp_spec[gindex + (window * window_increment) + j + bin + 1] = spec_coeff[k + 1]
         temp_spec[gindex + (window * window_increment) + j + bin + 2] = spec_coeff[k + 2]
         temp_spec[gindex + (window * window_increment) + j + bin + 3] = spec_coeff[k + 3]
         gincrease += 4
         k += 4
     j += width
   gindex + gincrease
 copy temp_spec -> spec_coeff

pns_decode()

ms_decode()

is_decode()

ic_prediction()

pns_reset_pred_state()

tns_decode_frame()

drc_decode()

ifilter_bank()

imdct_long()

In low complexity mode, this function simply calls down to faad_imdct(). If LD is enabled, there is some more processing beforehand.

faad_imdct(real *in, real *out)

This comment appears in FAAD2's mdct.c file header:

"Fast (I)MDCT Implementation using (I)FFT ((Inverse) Fast Fourier Transform) and consists of three steps: pre-(I)FFT complex multiplication, complex (I)FFT, post-(I)FFT complex multiplication."

This function is apparently a good candidate for SIMD optimization.

ssr_decode()

lt_update_state()

sbrDecodeInit()

sbrDecodeCoupleFrame()