static int vorbis_parse_audio_packet(vorbis_context *vc)

{

    GetBitContext *gb = &vc->gb;

    FFTContext *mdct;

    unsigned previous_window = vc->previous_window;

    unsigned mode_number, blockflag, blocksize;

    int i, j;

    uint8_t no_residue[255];

    uint8_t do_not_decode[255];

    vorbis_mapping *mapping;

    float *ch_res_ptr   = vc->channel_residues;

    float *ch_floor_ptr = vc->channel_floors;

    uint8_t res_chan[255];

    unsigned res_num = 0;

    int retlen  = 0;

    int ch_left = vc->audio_channels;



    if (get_bits1(gb)) {

        av_log(vc->avccontext, AV_LOG_ERROR, "Not a Vorbis I audio packet.\n");

        return AVERROR_INVALIDDATA; // packet type not audio

    }



    if (vc->mode_count == 1) {

        mode_number = 0;

    } else {

        GET_VALIDATED_INDEX(mode_number, ilog(vc->mode_count-1), vc->mode_count)

    }

    vc->mode_number = mode_number;

    mapping = &vc->mappings[vc->modes[mode_number].mapping];



    av_dlog(NULL, " Mode number: %u , mapping: %d , blocktype %d\n", mode_number,

            vc->modes[mode_number].mapping, vc->modes[mode_number].blockflag);



    blockflag = vc->modes[mode_number].blockflag;

    blocksize = vc->blocksize[blockflag];

    if (blockflag)

        skip_bits(gb, 2); // previous_window, next_window



    memset(ch_res_ptr,   0, sizeof(float) * vc->audio_channels * blocksize / 2); //FIXME can this be removed ?

    memset(ch_floor_ptr, 0, sizeof(float) * vc->audio_channels * blocksize / 2); //FIXME can this be removed ?



// Decode floor



    for (i = 0; i < vc->audio_channels; ++i) {

        vorbis_floor *floor;

        int ret;

        if (mapping->submaps > 1) {

            floor = &vc->floors[mapping->submap_floor[mapping->mux[i]]];

        } else {

            floor = &vc->floors[mapping->submap_floor[0]];

        }



        ret = floor->decode(vc, &floor->data, ch_floor_ptr);



        if (ret < 0) {

            av_log(vc->avccontext, AV_LOG_ERROR, "Invalid codebook in vorbis_floor_decode.\n");

            return AVERROR_INVALIDDATA;

        }

        no_residue[i] = ret;

        ch_floor_ptr += blocksize / 2;

    }



// Nonzero vector propagate



    for (i = mapping->coupling_steps - 1; i >= 0; --i) {

        if (!(no_residue[mapping->magnitude[i]] & no_residue[mapping->angle[i]])) {

            no_residue[mapping->magnitude[i]] = 0;

            no_residue[mapping->angle[i]]     = 0;

        }

    }



// Decode residue



    for (i = 0; i < mapping->submaps; ++i) {

        vorbis_residue *residue;

        unsigned ch = 0;



        for (j = 0; j < vc->audio_channels; ++j) {

            if ((mapping->submaps == 1) || (i == mapping->mux[j])) {

                res_chan[j] = res_num;

                if (no_residue[j]) {

                    do_not_decode[ch] = 1;

                } else {

                    do_not_decode[ch] = 0;

                }

                ++ch;

                ++res_num;

            }

        }

        residue = &vc->residues[mapping->submap_residue[i]];

        if (ch_left < ch) {

            av_log(vc->avccontext, AV_LOG_ERROR, "Too many channels in vorbis_floor_decode.\n");

            return -1;

        }

        vorbis_residue_decode(vc, residue, ch, do_not_decode, ch_res_ptr, blocksize/2);



        ch_res_ptr += ch * blocksize / 2;

        ch_left -= ch;

    }



// Inverse coupling



    for (i = mapping->coupling_steps - 1; i >= 0; --i) { //warning: i has to be signed

        float *mag, *ang;



        mag = vc->channel_residues+res_chan[mapping->magnitude[i]] * blocksize / 2;

        ang = vc->channel_residues+res_chan[mapping->angle[i]]     * blocksize / 2;

        vc->dsp.vorbis_inverse_coupling(mag, ang, blocksize / 2);

    }



// Dotproduct, MDCT



    mdct = &vc->mdct[blockflag];



    for (j = vc->audio_channels-1;j >= 0; j--) {

        ch_floor_ptr = vc->channel_floors   + j           * blocksize / 2;

        ch_res_ptr   = vc->channel_residues + res_chan[j] * blocksize / 2;

        vc->dsp.vector_fmul(ch_floor_ptr, ch_floor_ptr, ch_res_ptr, blocksize / 2);

        mdct->imdct_half(mdct, ch_res_ptr, ch_floor_ptr);

    }



// Overlap/add, save data for next overlapping  FPMATH



    retlen = (blocksize + vc->blocksize[previous_window]) / 4;

    for (j = 0; j < vc->audio_channels; j++) {

        unsigned bs0 = vc->blocksize[0];

        unsigned bs1 = vc->blocksize[1];

        float *residue    = vc->channel_residues + res_chan[j] * blocksize / 2;

        float *saved      = vc->saved + j * bs1 / 4;

        float *ret        = vc->channel_floors + j * retlen;

        float *buf        = residue;

        const float *win  = vc->win[blockflag & previous_window];



        if (blockflag == previous_window) {

            vc->dsp.vector_fmul_window(ret, saved, buf, win, blocksize / 4);

        } else if (blockflag > previous_window) {

            vc->dsp.vector_fmul_window(ret, saved, buf, win, bs0 / 4);

            memcpy(ret+bs0/2, buf+bs0/4, ((bs1-bs0)/4) * sizeof(float));

        } else {

            memcpy(ret, saved, ((bs1 - bs0) / 4) * sizeof(float));

            vc->dsp.vector_fmul_window(ret + (bs1 - bs0) / 4, saved + (bs1 - bs0) / 4, buf, win, bs0 / 4);

        }

        memcpy(saved, buf + blocksize / 4, blocksize / 4 * sizeof(float));

    }



    vc->previous_window = blockflag;

    return retlen;

}
