static int wavpack_encode_block(WavPackEncodeContext *s,

                                int32_t *samples_l, int32_t *samples_r,

                                uint8_t *out, int out_size)

{

    int block_size, start, end, data_size, tcount, temp, m = 0;

    int i, j, ret = 0, got_extra = 0, nb_samples = s->block_samples;

    uint32_t crc = 0xffffffffu;

    struct Decorr *dpp;

    PutByteContext pb;



    if (!(s->flags & WV_MONO) && s->optimize_mono) {

        int32_t lor = 0, diff = 0;



        for (i = 0; i < nb_samples; i++) {

            lor  |= samples_l[i] | samples_r[i];

            diff |= samples_l[i] - samples_r[i];



            if (lor && diff)

                break;

        }



        if (i == nb_samples && lor && !diff) {

            s->flags &= ~(WV_JOINT_STEREO | WV_CROSS_DECORR);

            s->flags |= WV_FALSE_STEREO;



            if (!s->false_stereo) {

                s->false_stereo = 1;

                s->num_terms = 0;

                CLEAR(s->w);

            }

        } else if (s->false_stereo) {

            s->false_stereo = 0;

            s->num_terms = 0;

            CLEAR(s->w);

        }

    }



    if (s->flags & SHIFT_MASK) {

        int shift = (s->flags & SHIFT_MASK) >> SHIFT_LSB;

        int mag = (s->flags & MAG_MASK) >> MAG_LSB;



        if (s->flags & WV_MONO_DATA)

            shift_mono(samples_l, nb_samples, shift);

        else

            shift_stereo(samples_l, samples_r, nb_samples, shift);



        if ((mag -= shift) < 0)

            s->flags &= ~MAG_MASK;

        else

            s->flags -= (1 << MAG_LSB) * shift;

    }



    if ((s->flags & WV_FLOAT_DATA) || (s->flags & MAG_MASK) >> MAG_LSB >= 24) {

        av_fast_padded_malloc(&s->orig_l, &s->orig_l_size, sizeof(int32_t) * nb_samples);

        memcpy(s->orig_l, samples_l, sizeof(int32_t) * nb_samples);

        if (!(s->flags & WV_MONO_DATA)) {

            av_fast_padded_malloc(&s->orig_r, &s->orig_r_size, sizeof(int32_t) * nb_samples);

            memcpy(s->orig_r, samples_r, sizeof(int32_t) * nb_samples);

        }



        if (s->flags & WV_FLOAT_DATA)

            got_extra = scan_float(s, samples_l, samples_r, nb_samples);

        else

            got_extra = scan_int32(s, samples_l, samples_r, nb_samples);

        s->num_terms = 0;

    } else {

        scan_int23(s, samples_l, samples_r, nb_samples);

        if (s->shift != s->int32_zeros + s->int32_ones + s->int32_dups) {

            s->shift = s->int32_zeros + s->int32_ones + s->int32_dups;

            s->num_terms = 0;

        }

    }



    if (!s->num_passes && !s->num_terms) {

        s->num_passes = 1;



        if (s->flags & WV_MONO_DATA)

            ret = wv_mono(s, samples_l, 1, 0);

        else

            ret = wv_stereo(s, samples_l, samples_r, 1, 0);



        s->num_passes = 0;

    }

    if (s->flags & WV_MONO_DATA) {

        for (i = 0; i < nb_samples; i++)

            crc += (crc << 1) + samples_l[i];



        if (s->num_passes)

            ret = wv_mono(s, samples_l, !s->num_terms, 1);

    } else {

        for (i = 0; i < nb_samples; i++)

            crc += (crc << 3) + (samples_l[i] << 1) + samples_l[i] + samples_r[i];



        if (s->num_passes)

            ret = wv_stereo(s, samples_l, samples_r, !s->num_terms, 1);

    }

    if (ret < 0)

        return ret;



    if (!s->ch_offset)

        s->flags |= WV_INITIAL_BLOCK;



    s->ch_offset += 1 + !(s->flags & WV_MONO);



    if (s->ch_offset == s->avctx->channels)

        s->flags |= WV_FINAL_BLOCK;



    bytestream2_init_writer(&pb, out, out_size);

    bytestream2_put_le32(&pb, MKTAG('w', 'v', 'p', 'k'));

    bytestream2_put_le32(&pb, 0);

    bytestream2_put_le16(&pb, 0x410);

    bytestream2_put_le16(&pb, 0);

    bytestream2_put_le32(&pb, 0);

    bytestream2_put_le32(&pb, s->sample_index);

    bytestream2_put_le32(&pb, nb_samples);

    bytestream2_put_le32(&pb, s->flags);

    bytestream2_put_le32(&pb, crc);



    if (s->flags & WV_INITIAL_BLOCK &&

        s->avctx->channel_layout != AV_CH_LAYOUT_MONO &&

        s->avctx->channel_layout != AV_CH_LAYOUT_STEREO) {

        put_metadata_block(&pb, WP_ID_CHANINFO, 5);

        bytestream2_put_byte(&pb, s->avctx->channels);

        bytestream2_put_le32(&pb, s->avctx->channel_layout);

        bytestream2_put_byte(&pb, 0);

    }



    if ((s->flags & SRATE_MASK) == SRATE_MASK) {

        put_metadata_block(&pb, WP_ID_SAMPLE_RATE, 3);

        bytestream2_put_le24(&pb, s->avctx->sample_rate);

        bytestream2_put_byte(&pb, 0);

    }



    put_metadata_block(&pb, WP_ID_DECTERMS, s->num_terms);

    for (i = 0; i < s->num_terms; i++) {

        struct Decorr *dpp = &s->decorr_passes[i];

        bytestream2_put_byte(&pb, ((dpp->value + 5) & 0x1f) | ((dpp->delta << 5) & 0xe0));

    }

    if (s->num_terms & 1)

        bytestream2_put_byte(&pb, 0);



#define WRITE_DECWEIGHT(type) do {            \

        temp = store_weight(type);    \

        bytestream2_put_byte(&pb, temp);      \

        type = restore_weight(temp);  \

    } while (0)



    bytestream2_put_byte(&pb, WP_ID_DECWEIGHTS);

    bytestream2_put_byte(&pb, 0);

    start = bytestream2_tell_p(&pb);

    for (i = s->num_terms - 1; i >= 0; --i) {

        struct Decorr *dpp = &s->decorr_passes[i];



        if (store_weight(dpp->weightA) ||

            (!(s->flags & WV_MONO_DATA) && store_weight(dpp->weightB)))

                break;

    }

    tcount = i + 1;

    for (i = 0; i < s->num_terms; i++) {

        struct Decorr *dpp = &s->decorr_passes[i];

        if (i < tcount) {

            WRITE_DECWEIGHT(dpp->weightA);

            if (!(s->flags & WV_MONO_DATA))

                WRITE_DECWEIGHT(dpp->weightB);

        } else {

            dpp->weightA = dpp->weightB = 0;

        }

    }

    end = bytestream2_tell_p(&pb);

    out[start - 2] = WP_ID_DECWEIGHTS | (((end - start) & 1) ? WP_IDF_ODD: 0);

    out[start - 1] = (end - start + 1) >> 1;

    if ((end - start) & 1)

        bytestream2_put_byte(&pb, 0);



#define WRITE_DECSAMPLE(type) do {        \

        temp = log2s(type);               \

        type = wp_exp2(temp);             \

        bytestream2_put_le16(&pb, temp);  \

    } while (0)



    bytestream2_put_byte(&pb, WP_ID_DECSAMPLES);

    bytestream2_put_byte(&pb, 0);

    start = bytestream2_tell_p(&pb);

    for (i = 0; i < s->num_terms; i++) {

        struct Decorr *dpp = &s->decorr_passes[i];

        if (i == 0) {

            if (dpp->value > MAX_TERM) {

                WRITE_DECSAMPLE(dpp->samplesA[0]);

                WRITE_DECSAMPLE(dpp->samplesA[1]);

                if (!(s->flags & WV_MONO_DATA)) {

                    WRITE_DECSAMPLE(dpp->samplesB[0]);

                    WRITE_DECSAMPLE(dpp->samplesB[1]);

                }

            } else if (dpp->value < 0) {

                WRITE_DECSAMPLE(dpp->samplesA[0]);

                WRITE_DECSAMPLE(dpp->samplesB[0]);

            } else {

                for (j = 0; j < dpp->value; j++) {

                    WRITE_DECSAMPLE(dpp->samplesA[j]);

                    if (!(s->flags & WV_MONO_DATA))

                        WRITE_DECSAMPLE(dpp->samplesB[j]);

                }

            }

        } else {

            CLEAR(dpp->samplesA);

            CLEAR(dpp->samplesB);

        }

    }

    end = bytestream2_tell_p(&pb);

    out[start - 1] = (end - start) >> 1;



#define WRITE_CHAN_ENTROPY(chan) do {               \

        for (i = 0; i < 3; i++) {                   \

            temp = wp_log2(s->w.c[chan].median[i]); \

            bytestream2_put_le16(&pb, temp);        \

            s->w.c[chan].median[i] = wp_exp2(temp); \

        }                                           \

    } while (0)



    put_metadata_block(&pb, WP_ID_ENTROPY, 6 * (1 + (!(s->flags & WV_MONO_DATA))));

    WRITE_CHAN_ENTROPY(0);

    if (!(s->flags & WV_MONO_DATA))

        WRITE_CHAN_ENTROPY(1);



    if (s->flags & WV_FLOAT_DATA) {

        put_metadata_block(&pb, WP_ID_FLOATINFO, 4);

        bytestream2_put_byte(&pb, s->float_flags);

        bytestream2_put_byte(&pb, s->float_shift);

        bytestream2_put_byte(&pb, s->float_max_exp);

        bytestream2_put_byte(&pb, 127);

    }



    if (s->flags & WV_INT32_DATA) {

        put_metadata_block(&pb, WP_ID_INT32INFO, 4);

        bytestream2_put_byte(&pb, s->int32_sent_bits);

        bytestream2_put_byte(&pb, s->int32_zeros);

        bytestream2_put_byte(&pb, s->int32_ones);

        bytestream2_put_byte(&pb, s->int32_dups);

    }



    if (s->flags & WV_MONO_DATA && !s->num_passes) {

        for (i = 0; i < nb_samples; i++) {

            int32_t code = samples_l[i];



            for (tcount = s->num_terms, dpp = s->decorr_passes; tcount--; dpp++) {

                int32_t sam;



                if (dpp->value > MAX_TERM) {

                    if (dpp->value & 1)

                        sam = 2 * dpp->samplesA[0] - dpp->samplesA[1];

                    else

                        sam = (3 * dpp->samplesA[0] - dpp->samplesA[1]) >> 1;



                    dpp->samplesA[1] = dpp->samplesA[0];

                    dpp->samplesA[0] = code;

                } else {

                    sam = dpp->samplesA[m];

                    dpp->samplesA[(m + dpp->value) & (MAX_TERM - 1)] = code;

                }



                code -= APPLY_WEIGHT(dpp->weightA, sam);

                UPDATE_WEIGHT(dpp->weightA, dpp->delta, sam, code);

            }



            m = (m + 1) & (MAX_TERM - 1);

            samples_l[i] = code;

        }

        if (m) {

            for (tcount = s->num_terms, dpp = s->decorr_passes; tcount--; dpp++)

                if (dpp->value > 0 && dpp->value <= MAX_TERM) {

                int32_t temp_A[MAX_TERM], temp_B[MAX_TERM];

                int k;



                memcpy(temp_A, dpp->samplesA, sizeof(dpp->samplesA));

                memcpy(temp_B, dpp->samplesB, sizeof(dpp->samplesB));



                for (k = 0; k < MAX_TERM; k++) {

                    dpp->samplesA[k] = temp_A[m];

                    dpp->samplesB[k] = temp_B[m];

                    m = (m + 1) & (MAX_TERM - 1);

                }

            }

        }

    } else if (!s->num_passes) {

        if (s->flags & WV_JOINT_STEREO) {

            for (i = 0; i < nb_samples; i++)

                samples_r[i] += ((samples_l[i] -= samples_r[i]) >> 1);

        }



        for (i = 0; i < s->num_terms; i++) {

            struct Decorr *dpp = &s->decorr_passes[i];

            if (((s->flags & MAG_MASK) >> MAG_LSB) >= 16 || dpp->delta != 2)

                decorr_stereo_pass2(dpp, samples_l, samples_r, nb_samples);

            else

                decorr_stereo_pass_id2(dpp, samples_l, samples_r, nb_samples);

        }

    }



    bytestream2_put_byte(&pb, WP_ID_DATA | WP_IDF_LONG);

    init_put_bits(&s->pb, pb.buffer + 3, bytestream2_get_bytes_left_p(&pb));

    if (s->flags & WV_MONO_DATA) {

        for (i = 0; i < nb_samples; i++)

            wavpack_encode_sample(s, &s->w.c[0], s->samples[0][i]);

    } else {

        for (i = 0; i < nb_samples; i++) {

            wavpack_encode_sample(s, &s->w.c[0], s->samples[0][i]);

            wavpack_encode_sample(s, &s->w.c[1], s->samples[1][i]);

        }

    }

    encode_flush(s);

    flush_put_bits(&s->pb);

    data_size = put_bits_count(&s->pb) >> 3;

    bytestream2_put_le24(&pb, (data_size + 1) >> 1);

    bytestream2_skip_p(&pb, data_size);

    if (data_size & 1)

        bytestream2_put_byte(&pb, 0);



    if (got_extra) {

        bytestream2_put_byte(&pb, WP_ID_EXTRABITS | WP_IDF_LONG);

        init_put_bits(&s->pb, pb.buffer + 7, bytestream2_get_bytes_left_p(&pb));

        if (s->flags & WV_FLOAT_DATA)

            pack_float(s, s->orig_l, s->orig_r, nb_samples);

        else

            pack_int32(s, s->orig_l, s->orig_r, nb_samples);

        flush_put_bits(&s->pb);

        data_size = put_bits_count(&s->pb) >> 3;

        bytestream2_put_le24(&pb, (data_size + 5) >> 1);

        bytestream2_put_le32(&pb, s->crc_x);

        bytestream2_skip_p(&pb, data_size);

        if (data_size & 1)

            bytestream2_put_byte(&pb, 0);

    }



    block_size = bytestream2_tell_p(&pb);

    AV_WL32(out + 4, block_size - 8);



    av_assert0(put_bits_left(&s->pb) > 0);



    return block_size;

}
