unsafe void decode_residual(BitReader bitreader, FlacFrame frame, int ch)
        {
            // rice-encoded block
            // coding method
            frame.subframes[ch].best.rc.coding_method = (int)bitreader.readbits(2); // ????? == 0
            if (frame.subframes[ch].best.rc.coding_method != 0 && frame.subframes[ch].best.rc.coding_method != 1)
                throw new Exception("unsupported residual coding");
            // partition order
            frame.subframes[ch].best.rc.porder = (int)bitreader.readbits(4);
            if (frame.subframes[ch].best.rc.porder > 8)
                throw new Exception("invalid partition order");
            int psize = frame.blocksize >> frame.subframes[ch].best.rc.porder;
            int res_cnt = psize - frame.subframes[ch].best.order;

            int rice_len = 4 + frame.subframes[ch].best.rc.coding_method;
            // residual
            int j = frame.subframes[ch].best.order;
            int* r = frame.subframes[ch].best.residual + j;
            for (int p = 0; p < (1 << frame.subframes[ch].best.rc.porder); p++)
            {
                if (p == 1) res_cnt = psize;
                int n = Math.Min(res_cnt, frame.blocksize - j);

                int k = frame.subframes[ch].best.rc.rparams[p] = (int)bitreader.readbits(rice_len);
                if (k == (1 << rice_len) - 1)
                {
                    k = frame.subframes[ch].best.rc.esc_bps[p] = (int)bitreader.readbits(5);
                    for (int i = n; i > 0; i--)
                        *(r++) = bitreader.readbits_signed((int)k);
                }
                else
                {
                    bitreader.read_rice_block(n, (int)k, r);
                    r += n;
                }
                j += n;
            }
        }